envio 3.3.0-alpha.10 → 3.3.0-alpha.12

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 (60) hide show
  1. package/licenses/CLA.md +35 -0
  2. package/licenses/EULA.md +67 -0
  3. package/licenses/LICENSE.md +45 -0
  4. package/licenses/README.md +35 -0
  5. package/package.json +9 -8
  6. package/src/Batch.res.mjs +1 -1
  7. package/src/ChainFetching.res +30 -18
  8. package/src/ChainFetching.res.mjs +23 -13
  9. package/src/ChainState.res +45 -30
  10. package/src/ChainState.res.mjs +22 -8
  11. package/src/ChainState.resi +5 -0
  12. package/src/Config.res +9 -5
  13. package/src/Config.res.mjs +2 -2
  14. package/src/Core.res +20 -0
  15. package/src/Core.res.mjs +12 -0
  16. package/src/CrossChainState.res +64 -29
  17. package/src/CrossChainState.res.mjs +17 -8
  18. package/src/EventConfigBuilder.res +21 -46
  19. package/src/EventConfigBuilder.res.mjs +18 -25
  20. package/src/EventProcessing.res +8 -5
  21. package/src/EventProcessing.res.mjs +2 -1
  22. package/src/FetchState.res +428 -322
  23. package/src/FetchState.res.mjs +316 -233
  24. package/src/HandlerRegister.res +3 -1
  25. package/src/HandlerRegister.res.mjs +3 -2
  26. package/src/Internal.res +11 -2
  27. package/src/LogSelection.res +0 -62
  28. package/src/LogSelection.res.mjs +0 -74
  29. package/src/RawEvent.res +2 -2
  30. package/src/Rollback.res +32 -13
  31. package/src/Rollback.res.mjs +24 -11
  32. package/src/SimulateDeadInputTracker.res +1 -1
  33. package/src/SimulateItems.res +38 -6
  34. package/src/SimulateItems.res.mjs +28 -9
  35. package/src/TestIndexer.res +2 -2
  36. package/src/TestIndexer.res.mjs +2 -2
  37. package/src/TopicFilter.res +1 -25
  38. package/src/TopicFilter.res.mjs +0 -74
  39. package/src/bindings/Viem.res +0 -5
  40. package/src/sources/EventRouter.res +0 -21
  41. package/src/sources/EventRouter.res.mjs +0 -12
  42. package/src/sources/EvmChain.res +2 -27
  43. package/src/sources/EvmChain.res.mjs +2 -23
  44. package/src/sources/EvmRpcClient.res +12 -15
  45. package/src/sources/EvmRpcClient.res.mjs +3 -3
  46. package/src/sources/HyperSync.res +9 -38
  47. package/src/sources/HyperSync.res.mjs +16 -28
  48. package/src/sources/HyperSync.resi +2 -2
  49. package/src/sources/HyperSyncClient.res +88 -11
  50. package/src/sources/HyperSyncClient.res.mjs +39 -6
  51. package/src/sources/HyperSyncSource.res +18 -199
  52. package/src/sources/HyperSyncSource.res.mjs +9 -128
  53. package/src/sources/Rpc.res +0 -32
  54. package/src/sources/Rpc.res.mjs +1 -46
  55. package/src/sources/RpcSource.res +29 -175
  56. package/src/sources/RpcSource.res.mjs +32 -110
  57. package/src/sources/SimulateSource.res +37 -19
  58. package/src/sources/SimulateSource.res.mjs +27 -10
  59. package/src/sources/SvmHyperSyncSource.res +13 -3
  60. package/src/sources/SvmHyperSyncSource.res.mjs +1 -1
@@ -115,10 +115,10 @@ let svmEventDescriptorSchema = S$RescriptSchema.schema(s => ({
115
115
  transactionFields: s.m(S$RescriptSchema.array(Internal.svmTransactionFieldSchema)),
116
116
  blockFields: s.m(S$RescriptSchema.option(S$RescriptSchema.array(Internal.svmBlockFieldSchema))),
117
117
  includeLogs: s.m(S$RescriptSchema.bool),
118
- accountFilters: s.m(S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.schema(s => ({
118
+ accountFilters: s.m(S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.array(S$RescriptSchema.schema(s => ({
119
119
  position: s.m(S$RescriptSchema.int),
120
120
  values: s.m(S$RescriptSchema.array(S$RescriptSchema.string))
121
- }))))),
121
+ })))))),
122
122
  isInner: s.m(S$RescriptSchema.option(S$RescriptSchema.bool)),
123
123
  accounts: s.m(S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.string))),
124
124
  args: s.m(S$RescriptSchema.option(S$RescriptSchema.json(false)))
package/src/Core.res CHANGED
@@ -10,9 +10,17 @@ type svmHypersyncClientCtor
10
10
  type hyperfuelClientCtor
11
11
  type transactionStoreCtor
12
12
  type blockStoreCtor
13
+ type parseConfigYamlOptions = {
14
+ schema?: string,
15
+ env?: dict<string>,
16
+ files?: dict<string>,
17
+ isRescript?: bool,
18
+ }
13
19
 
14
20
  type addon = {
15
21
  getConfigJson: (~configPath: Null.t<string>, ~directory: Null.t<string>) => string,
22
+ encodeIndexedTopic: (~abiType: string, ~value: unknown) => EvmTypes.Hex.t,
23
+ parseConfigYaml: (string, parseConfigYamlOptions) => string,
16
24
  runCli: (~args: array<string>, ~envioPackageDir: Null.t<string>) => promise<Null.t<string>>,
17
25
  @as("EvmHypersyncClient")
18
26
  evmHypersyncClient: evmHypersyncClientCtor,
@@ -197,6 +205,18 @@ let getConfigJson = (~configPath=?, ~directory=?) => {
197
205
  )
198
206
  }
199
207
 
208
+ // Pure config entry point: no cwd, config file, schema file, .env, or process env lookup.
209
+ // A supplied schema is authoritative; omitted or blank schema text means an empty schema.
210
+ let parseConfigYaml = (~schema=?, ~env=?, ~files=?, ~isRescript=false, yaml) => {
211
+ let addon = getAddon()
212
+ addon.parseConfigYaml(yaml, {
213
+ ?schema,
214
+ ?env,
215
+ ?files,
216
+ isRescript,
217
+ })
218
+ }
219
+
200
220
  let runCli = args => {
201
221
  let addon = getAddon()
202
222
  addon.runCli(~args, ~envioPackageDir=Null.make(envioPackageDir))
package/src/Core.res.mjs CHANGED
@@ -180,6 +180,17 @@ function getConfigJson(configPath, directory) {
180
180
  return addon.getConfigJson(Stdlib_Null.fromOption(configPath), Stdlib_Null.fromOption(directory));
181
181
  }
182
182
 
183
+ function parseConfigYaml(schema, env, files, isRescriptOpt, yaml) {
184
+ let isRescript = isRescriptOpt !== undefined ? isRescriptOpt : false;
185
+ let addon = getAddon();
186
+ return addon.parseConfigYaml(yaml, {
187
+ schema: schema,
188
+ env: env,
189
+ files: files,
190
+ isRescript: isRescript
191
+ });
192
+ }
193
+
183
194
  function runCli(args) {
184
195
  let addon = getAddon();
185
196
  return addon.runCli(args, envioPackageDir);
@@ -196,6 +207,7 @@ export {
196
207
  addonRef,
197
208
  getAddon,
198
209
  getConfigJson,
210
+ parseConfigYaml,
199
211
  runCli,
200
212
  }
201
213
  /* envioPackageDir Not a pure module */
@@ -170,7 +170,7 @@ let applyBatchProgress = (crossChainState: t, ~batch: Batch.t, ~blockTimestampNa
170
170
  // --- Fetch control. ---
171
171
 
172
172
  // Chains ordered furthest-behind first, so the shared buffer pool goes to the
173
- // chains with the most backfill work before the rest.
173
+ // chains with the most fetchable backfill work before the rest.
174
174
  let priorityOrder = (crossChainState: t) =>
175
175
  crossChainState.chainStates
176
176
  ->Dict.valuesToArray
@@ -198,29 +198,37 @@ let totalReservedSize = (crossChainState: t) => {
198
198
  // chain-density-derived target block, then subtract what it actually used
199
199
  // before moving to the next chain. So a chain that can only use a little
200
200
  // (density too low, or already caught up) leaves the rest for the others
201
- // automatically. A chain visited after the budget ran out simply doesn't
202
- // query this round the leader's reservations release as its responses land,
203
- // so the next tick redistributes. Chains that do get budget are additionally
204
- // capped at the most-behind chain's target progress mapped onto their own
205
- // range, so no chain runs further ahead than the chain the pool is
206
- // prioritizing.
201
+ // automatically. Starting a new query requires at least 10% of the target pool
202
+ // to be free. A chain visited after the budget falls below that admission unit
203
+ // doesn't query this round reservations release as responses land, so the
204
+ // next tick redistributes. Chains that do get budget are additionally capped
205
+ // at the first querying chain's target progress mapped onto their own range, so
206
+ // no chain runs further ahead than the chain currently using the pool. Waiting
207
+ // and idle chains do not establish this alignment line.
207
208
  let checkAndFetch = async (
208
209
  crossChainState: t,
209
210
  ~dispatchChain: (~chain: ChainMap.Chain.t, ~action: FetchState.nextQuery) => promise<unit>,
210
211
  ) => {
212
+ let targetBudget = crossChainState.targetBufferSize->Int.toFloat
211
213
  let remaining = ref(
212
214
  Pervasives.max(
213
215
  0.,
214
- crossChainState.targetBufferSize->Int.toFloat -.
216
+ targetBudget -.
215
217
  crossChainState->totalReadyCount->Int.toFloat -.
216
218
  crossChainState->totalReservedSize,
217
219
  ),
218
220
  )
219
221
 
222
+ // New fetch work is admitted in units of 10% of the target pool. Waiting
223
+ // below this floor avoids spending the last few free items on undersized
224
+ // queries; response and batch completion schedule another tick after they
225
+ // release enough budget.
226
+ let minimumAdmissionBudget = targetBudget *. 0.1
227
+
220
228
  // A chain with no density signal probes blind, so it only gets a bounded
221
229
  // slice of the pool — one unknown chain shouldn't hold the whole budget
222
- // while it takes its first measurements.
223
- let coldChainBudget = Pervasives.min(5_000., crossChainState.targetBufferSize->Int.toFloat)
230
+ // while it takes its first measurements. Its probe is one admission unit.
231
+ let coldChainBudget = minimumAdmissionBudget
224
232
 
225
233
  // Chunk reservations get headroom over the density estimate so a
226
234
  // denser-than-expected range doesn't truncate at the server cap; realtime
@@ -233,30 +241,33 @@ let checkAndFetch = async (
233
241
  ->priorityOrder
234
242
  ->Array.forEach(cs => {
235
243
  let chainId = (cs->ChainState.chainConfig).id
236
- if cs->ChainState.knownHeight == 0 {
237
- // No height yet nothing to size a budget or an alignment line
238
- // against. Skip without consuming budget or claiming leadership, so a
239
- // chain whose source hasn't reported doesn't unconstrain everyone else.
240
- actionByChain->Utils.Dict.setByInt(chainId, FetchState.WaitingForNewBlock)
244
+ if remaining.contents < minimumAdmissionBudget {
245
+ // More than 90% of the target pool is ready or reserved. Do not attempt
246
+ // any chain action until a full admission unit becomes free.
247
+ actionByChain->Utils.Dict.setByInt(chainId, FetchState.NothingToQuery)
248
+ } else if cs->ChainState.knownHeight == 0 {
249
+ // Let getNextQuery own the waiting decision without trying to size work
250
+ // against an unknown height.
251
+ actionByChain->Utils.Dict.setByInt(
252
+ chainId,
253
+ cs->ChainState.getNextQuery(~chainTargetItems=0., ~chunkItemsMultiplier),
254
+ )
241
255
  } else {
242
256
  let isCold = cs->ChainState.effectiveDensity === None
243
257
  let chainTargetItems =
244
258
  (isCold ? Pervasives.min(remaining.contents, coldChainBudget) : remaining.contents) +.
245
259
  cs->ChainState.pendingBudget
260
+ let candidateProgress = switch maxProgress.contents {
261
+ | None if !isCold =>
262
+ Some(
263
+ cs->ChainState.progressAtBlock(
264
+ ~blockNumber=cs->ChainState.targetBlock(~chainTargetItems),
265
+ ),
266
+ )
267
+ | _ => None
268
+ }
246
269
  let maxTargetBlock = switch maxProgress.contents {
247
- | None if isCold =>
248
- // A cold chain's target is a guess — it doesn't set the alignment
249
- // line; the first density-bearing chain after it does.
250
- None
251
- | None =>
252
- // The most-behind chain sets the alignment line for everyone after it.
253
- maxProgress :=
254
- Some(
255
- cs->ChainState.progressAtBlock(
256
- ~blockNumber=cs->ChainState.targetBlock(~chainTargetItems),
257
- ),
258
- )
259
- None
270
+ | None => None
260
271
  // 5% margin past the leader's line: chains whose progress tracks the
261
272
  // leader closely would otherwise flap in and out of the clamp as
262
273
  // leadership alternates, stalling their pipeline every other tick.
@@ -267,9 +278,33 @@ let checkAndFetch = async (
267
278
  ~chunkItemsMultiplier,
268
279
  ~maxTargetBlock?,
269
280
  ) {
270
- | (WaitingForNewBlock | NothingToQuery) as action =>
281
+ | WaitingForNewBlock as action => actionByChain->Utils.Dict.setByInt(chainId, action)
282
+ | NothingToQuery =>
283
+ // A chain below its head that emitted no query this tick — its budget
284
+ // went to more-behind chains, or the cross-chain alignment clamped its
285
+ // range to nothing — and has nothing else to wake it must keep polling
286
+ // for new blocks instead of going silent. NothingToQuery isn't
287
+ // dispatched, so such a chain would never be re-scheduled and its head
288
+ // tracking would freeze. A chain is genuinely idle — and correctly left
289
+ // undispatched — when it is caught up to its head/endblock, still
290
+ // draining in-flight queries, or holding ready items that batch
291
+ // processing will drain and re-schedule from.
292
+ let action =
293
+ cs->ChainState.isFetchingAtHead ||
294
+ cs->ChainState.pendingBudget > 0. ||
295
+ cs->ChainState.bufferReadyCount > 0
296
+ ? FetchState.NothingToQuery
297
+ : FetchState.WaitingForNewBlock
271
298
  actionByChain->Utils.Dict.setByInt(chainId, action)
272
299
  | Ready(queries) => {
300
+ // A density-bearing chain establishes the alignment line only after
301
+ // proving that it can use the pool. Waiting and idle chains leave it
302
+ // open for the next chain; cold-chain targets remain guesses and do
303
+ // not lead, as before.
304
+ switch candidateProgress {
305
+ | Some(progress) => maxProgress := Some(progress)
306
+ | None => ()
307
+ }
273
308
  let consumed =
274
309
  queries->Array.reduce(0., (acc, query: FetchState.query) =>
275
310
  acc +. query.itemsEst->Int.toFloat
@@ -134,10 +134,11 @@ function totalReservedSize(crossChainState) {
134
134
  }
135
135
 
136
136
  async function checkAndFetch(crossChainState, dispatchChain) {
137
+ let targetBudget = crossChainState.targetBufferSize;
137
138
  let remaining = {
138
- contents: Primitive_float.max(0, crossChainState.targetBufferSize - totalReadyCount(crossChainState) - totalReservedSize(crossChainState))
139
+ contents: Primitive_float.max(0, targetBudget - totalReadyCount(crossChainState) - totalReservedSize(crossChainState))
139
140
  };
140
- let coldChainBudget = Primitive_float.min(5000, crossChainState.targetBufferSize);
141
+ let minimumAdmissionBudget = targetBudget * 0.1;
141
142
  let chunkItemsMultiplier = crossChainState.isRealtime ? 3 : 1.5;
142
143
  let actionByChain = {};
143
144
  let maxProgress = {
@@ -145,28 +146,36 @@ async function checkAndFetch(crossChainState, dispatchChain) {
145
146
  };
146
147
  priorityOrder(crossChainState).forEach(cs => {
147
148
  let chainId = ChainState.chainConfig(cs).id;
149
+ if (remaining.contents < minimumAdmissionBudget) {
150
+ actionByChain[chainId] = "NothingToQuery";
151
+ return;
152
+ }
148
153
  if (ChainState.knownHeight(cs) === 0) {
149
- actionByChain[chainId] = "WaitingForNewBlock";
154
+ actionByChain[chainId] = ChainState.getNextQuery(cs, 0, chunkItemsMultiplier, undefined);
150
155
  return;
151
156
  }
152
157
  let isCold = ChainState.effectiveDensity(cs) === undefined;
153
158
  let chainTargetItems = (
154
- isCold ? Primitive_float.min(remaining.contents, coldChainBudget) : remaining.contents
159
+ isCold ? Primitive_float.min(remaining.contents, minimumAdmissionBudget) : remaining.contents
155
160
  ) + ChainState.pendingBudget(cs);
161
+ let match = maxProgress.contents;
162
+ let candidateProgress = match !== undefined || isCold ? undefined : ChainState.progressAtBlock(cs, ChainState.targetBlock(cs, chainTargetItems));
156
163
  let progress = maxProgress.contents;
157
- let maxTargetBlock = progress !== undefined ? ChainState.blockAtProgress(cs, progress + 0.05) : (
158
- isCold ? undefined : (maxProgress.contents = ChainState.progressAtBlock(cs, ChainState.targetBlock(cs, chainTargetItems)), undefined)
159
- );
164
+ let maxTargetBlock = progress !== undefined ? ChainState.blockAtProgress(cs, progress + 0.05) : undefined;
160
165
  let action = ChainState.getNextQuery(cs, chainTargetItems, chunkItemsMultiplier, maxTargetBlock);
161
166
  if (typeof action !== "object") {
162
167
  if (action === "WaitingForNewBlock") {
163
168
  actionByChain[chainId] = action;
164
169
  return;
165
170
  }
166
- actionByChain[chainId] = action;
171
+ let action$1 = ChainState.isFetchingAtHead(cs) || ChainState.pendingBudget(cs) > 0 || ChainState.bufferReadyCount(cs) > 0 ? "NothingToQuery" : "WaitingForNewBlock";
172
+ actionByChain[chainId] = action$1;
167
173
  return;
168
174
  } else {
169
175
  let queries = action._0;
176
+ if (candidateProgress !== undefined) {
177
+ maxProgress.contents = candidateProgress;
178
+ }
170
179
  let consumed = Stdlib_Array.reduce(queries, 0, (acc, query) => acc + query.itemsEst);
171
180
  let partitions = {};
172
181
  queries.forEach(query => {
@@ -236,58 +236,30 @@ let buildSimulateParamsSchema = (params: array<paramMeta>): S.t<Internal.eventPa
236
236
 
237
237
  // ============== Build topic filter getters ==============
238
238
 
239
- let getTopicEncoder = (abiType: string): (unknown => EvmTypes.Hex.t) => {
240
- // Handle array/tuple types - these get keccak256'd
241
- if abiType->String.endsWith("]") || abiType->String.startsWith("(") {
242
- TopicFilter.castToHexUnsafe->(Utils.magic: ('a => EvmTypes.Hex.t) => unknown => EvmTypes.Hex.t)
243
- } else {
244
- switch abiType {
245
- | "address" =>
246
- // Lowercase before encoding so mixed-case (checksummed) user input
247
- // still matches the lowercase hex topics returned by sources.
248
-
249
- (
250
- (value: string) =>
251
- value->String.toLowerCase->Address.unsafeFromString->TopicFilter.fromAddress
252
- )->(Utils.magic: (string => EvmTypes.Hex.t) => unknown => EvmTypes.Hex.t)
253
-
254
- | "bool" =>
255
- TopicFilter.fromBool->(Utils.magic: (bool => EvmTypes.Hex.t) => unknown => EvmTypes.Hex.t)
256
- | "string" =>
257
- TopicFilter.fromDynamicString->(
258
- Utils.magic: (string => EvmTypes.Hex.t) => unknown => EvmTypes.Hex.t
259
- )
260
-
261
- | "bytes" =>
262
- TopicFilter.fromDynamicBytes->(
263
- Utils.magic: (string => EvmTypes.Hex.t) => unknown => EvmTypes.Hex.t
264
- )
265
-
266
- | t if t->String.startsWith("uint") =>
267
- TopicFilter.fromBigInt->(Utils.magic: (bigint => EvmTypes.Hex.t) => unknown => EvmTypes.Hex.t)
268
- | t if t->String.startsWith("int") =>
269
- TopicFilter.fromSignedBigInt->(
270
- Utils.magic: (bigint => EvmTypes.Hex.t) => unknown => EvmTypes.Hex.t
271
- )
272
-
273
- | t if t->String.startsWith("bytes") =>
274
- TopicFilter.castToHexUnsafe->(
275
- Utils.magic: ('a => EvmTypes.Hex.t) => unknown => EvmTypes.Hex.t
276
- )
277
-
278
- | other => JsError.throwWithMessage(`Unsupported topic filter ABI type: ${other}`)
279
- }
280
- }
281
- }
239
+ let getTopicEncoder = (abiType: string): (unknown => EvmTypes.Hex.t) => value =>
240
+ Core.getAddon().encodeIndexedTopic(~abiType, ~value)
282
241
 
283
242
  let buildTopicGetter = (p: paramMeta) => {
284
243
  let encoder = getTopicEncoder(p.abiType)
244
+ let isTuple = p.abiType->String.startsWith("(")
285
245
  (eventFilter: dict<JSON.t>) =>
286
246
  eventFilter
287
247
  ->Utils.Dict.dangerouslyGetNonOption(p.name)
288
- ->Option.mapOr([], topicFilters =>
289
- topicFilters->(Utils.magic: JSON.t => unknown)->normalizeOrThrow->Array.map(encoder)
290
- )
248
+ ->Option.mapOr([], topicFilters => {
249
+ let raw = topicFilters->(Utils.magic: JSON.t => unknown)
250
+ // A tuple filter value is itself an array, so a directly-passed tuple is
251
+ // indistinguishable from an OR-list by shape alone. A single tuple is
252
+ // the common case, so try it first; when the value doesn't ABI-encode as
253
+ // one tuple it must be an OR-list of tuples.
254
+ if isTuple {
255
+ switch encoder(raw) {
256
+ | encoded => [encoded]
257
+ | exception _ => raw->normalizeOrThrow->Array.map(encoder)
258
+ }
259
+ } else {
260
+ raw->normalizeOrThrow->Array.map(encoder)
261
+ }
262
+ })
291
263
  }
292
264
 
293
265
  // ============== Field selection ==============
@@ -452,6 +424,7 @@ let buildEvmOnEventRegistration = (
452
424
  }
453
425
 
454
426
  {
427
+ index: -1,
455
428
  eventConfig: (eventConfig :> Internal.eventConfig),
456
429
  isWildcard,
457
430
  handler,
@@ -536,6 +509,7 @@ let buildSvmOnEventRegistration = (
536
509
  ~contractRegister: option<Internal.contractRegister>,
537
510
  ~startBlock: option<int>=?,
538
511
  ): Internal.svmOnEventRegistration => {
512
+ index: -1,
539
513
  eventConfig: (eventConfig :> Internal.eventConfig),
540
514
  handler,
541
515
  contractRegister,
@@ -613,6 +587,7 @@ let buildFuelOnEventRegistration = (
613
587
  ~contractRegister: option<Internal.contractRegister>,
614
588
  ~startBlock: option<int>=?,
615
589
  ): Internal.fuelOnEventRegistration => {
590
+ index: -1,
616
591
  eventConfig: (eventConfig :> Internal.eventConfig),
617
592
  handler,
618
593
  contractRegister,
@@ -2,11 +2,11 @@
2
2
 
3
3
  import * as Evm from "./sources/Evm.res.mjs";
4
4
  import * as Svm from "./sources/Svm.res.mjs";
5
+ import * as Core from "./Core.res.mjs";
5
6
  import * as Utils from "./Utils.res.mjs";
6
7
  import * as Address from "./Address.res.mjs";
7
8
  import * as FuelSDK from "./sources/FuelSDK.res.mjs";
8
9
  import * as Internal from "./Internal.res.mjs";
9
- import * as TopicFilter from "./TopicFilter.res.mjs";
10
10
  import * as LogSelection from "./LogSelection.res.mjs";
11
11
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
12
12
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
@@ -208,34 +208,24 @@ function buildSimulateParamsSchema(params) {
208
208
  }
209
209
 
210
210
  function getTopicEncoder(abiType) {
211
- if (abiType.endsWith("]") || abiType.startsWith("(")) {
212
- return TopicFilter.castToHexUnsafe;
213
- }
214
- switch (abiType) {
215
- case "address" :
216
- return value => TopicFilter.fromAddress(value.toLowerCase());
217
- case "bool" :
218
- return TopicFilter.fromBool;
219
- case "bytes" :
220
- return TopicFilter.fromDynamicBytes;
221
- case "string" :
222
- return TopicFilter.fromDynamicString;
223
- default:
224
- if (abiType.startsWith("uint")) {
225
- return TopicFilter.fromBigInt;
226
- } else if (abiType.startsWith("int")) {
227
- return TopicFilter.fromSignedBigInt;
228
- } else if (abiType.startsWith("bytes")) {
229
- return TopicFilter.castToHexUnsafe;
230
- } else {
231
- return Stdlib_JsError.throwWithMessage(`Unsupported topic filter ABI type: ` + abiType);
232
- }
233
- }
211
+ return value => Core.getAddon().encodeIndexedTopic(abiType, value);
234
212
  }
235
213
 
236
214
  function buildTopicGetter(p) {
237
215
  let encoder = getTopicEncoder(p.abiType);
238
- return eventFilter => Stdlib_Option.mapOr(eventFilter[p.name], [], topicFilters => normalizeOrThrow(topicFilters).map(encoder));
216
+ let isTuple = p.abiType.startsWith("(");
217
+ return eventFilter => Stdlib_Option.mapOr(eventFilter[p.name], [], topicFilters => {
218
+ if (!isTuple) {
219
+ return normalizeOrThrow(topicFilters).map(encoder);
220
+ }
221
+ let encoded;
222
+ try {
223
+ encoded = encoder(topicFilters);
224
+ } catch (exn) {
225
+ return normalizeOrThrow(topicFilters).map(encoder);
226
+ }
227
+ return [encoded];
228
+ });
239
229
  }
240
230
 
241
231
  let alwaysIncludedBlockFields = [
@@ -317,6 +307,7 @@ function buildEvmOnEventRegistration(eventConfig, isWildcard, handler, contractR
317
307
  let sb = resolvedWhere.startBlock;
318
308
  let resolvedStartBlock = sb !== undefined ? sb : startBlock;
319
309
  return {
310
+ index: -1,
320
311
  eventConfig: eventConfig,
321
312
  handler: handler,
322
313
  contractRegister: contractRegister,
@@ -369,6 +360,7 @@ function buildSvmInstructionEventConfig(contractName, instructionName, programId
369
360
 
370
361
  function buildSvmOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, startBlock) {
371
362
  return {
363
+ index: -1,
372
364
  eventConfig: eventConfig,
373
365
  handler: handler,
374
366
  contractRegister: contractRegister,
@@ -438,6 +430,7 @@ function buildFuelEventConfig(contractName, eventName, kind, sighash, rawAbi) {
438
430
 
439
431
  function buildFuelOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, startBlock) {
440
432
  return {
433
+ index: -1,
441
434
  eventConfig: eventConfig,
442
435
  handler: handler,
443
436
  contractRegister: contractRegister,
@@ -73,9 +73,10 @@ let runEventHandlerOrThrow = async (
73
73
  )
74
74
  }
75
75
  let handlerDuration = timeBeforeHandler->Performance.secondsSince
76
+ let eventConfig = eventItem.onEventRegistration.eventConfig
76
77
  Prometheus.ProcessingHandler.increment(
77
- ~contract=eventItem.onEventRegistration.eventConfig.contractName,
78
- ~event=eventItem.onEventRegistration.eventConfig.name,
78
+ ~contract=eventConfig.contractName,
79
+ ~event=eventConfig.name,
79
80
  ~duration=handlerDuration,
80
81
  )
81
82
  }
@@ -121,8 +122,8 @@ let runHandlerOrThrow = async (
121
122
  }),
122
123
  )
123
124
  }
124
- | Event({onEventRegistration}) =>
125
- switch onEventRegistration.handler {
125
+ | Event(_) =>
126
+ switch (item->Internal.castUnsafeEventItem).onEventRegistration.handler {
126
127
  | Some(handler) =>
127
128
  await item->runEventHandlerOrThrow(
128
129
  ~handler,
@@ -161,7 +162,9 @@ let preloadBatchOrThrow = async (
161
162
  for idx in 0 to checkpointEventsProcessed - 1 {
162
163
  let item = batch.items->Array.getUnsafe(itemIdx.contents + idx)
163
164
  switch item {
164
- | Event({onEventRegistration: {handler, eventConfig: {contractName, name: eventName}}}) =>
165
+ | Event(_) =>
166
+ let {handler, eventConfig: {contractName, name: eventName}} =
167
+ (item->Internal.castUnsafeEventItem).onEventRegistration
165
168
  switch handler {
166
169
  | None => ()
167
170
  | Some(handler) =>
@@ -65,7 +65,8 @@ async function runEventHandlerOrThrow(item, checkpointId, handler, indexerState,
65
65
  };
66
66
  }
67
67
  let handlerDuration = Performance.secondsSince(timeBeforeHandler);
68
- return Prometheus.ProcessingHandler.increment(item.onEventRegistration.eventConfig.contractName, item.onEventRegistration.eventConfig.name, handlerDuration);
68
+ let eventConfig = item.onEventRegistration.eventConfig;
69
+ return Prometheus.ProcessingHandler.increment(eventConfig.contractName, eventConfig.name, handlerDuration);
69
70
  }
70
71
 
71
72
  async function runHandlerOrThrow(item, checkpointId, indexerState, loadManager, persistence, config, chains) {