envio 3.3.0-alpha.7 → 3.3.0-alpha.9

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 (95) 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 +401 -171
  8. package/src/ChainState.res.mjs +270 -104
  9. package/src/ChainState.resi +19 -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 +82 -82
  14. package/src/CrossChainState.res.mjs +52 -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/EnvioGlobal.res +53 -0
  20. package/src/EnvioGlobal.res.mjs +31 -0
  21. package/src/EventConfigBuilder.res +117 -69
  22. package/src/EventConfigBuilder.res.mjs +71 -34
  23. package/src/EventProcessing.res +19 -15
  24. package/src/EventProcessing.res.mjs +13 -12
  25. package/src/FetchState.res +410 -185
  26. package/src/FetchState.res.mjs +268 -253
  27. package/src/HandlerLoader.res +8 -113
  28. package/src/HandlerLoader.res.mjs +2 -88
  29. package/src/HandlerRegister.res +518 -144
  30. package/src/HandlerRegister.res.mjs +285 -133
  31. package/src/HandlerRegister.resi +24 -8
  32. package/src/IndexerState.res +6 -3
  33. package/src/IndexerState.res.mjs +3 -3
  34. package/src/IndexerState.resi +1 -1
  35. package/src/IndexingAddresses.res +10 -7
  36. package/src/IndexingAddresses.res.mjs +8 -7
  37. package/src/IndexingAddresses.resi +3 -1
  38. package/src/Internal.res +104 -39
  39. package/src/Internal.res.mjs +11 -1
  40. package/src/LogSelection.res +102 -165
  41. package/src/LogSelection.res.mjs +101 -116
  42. package/src/Main.res +49 -164
  43. package/src/Main.res.mjs +39 -104
  44. package/src/Metrics.res +43 -2
  45. package/src/Metrics.res.mjs +39 -3
  46. package/src/Prometheus.res +0 -35
  47. package/src/Prometheus.res.mjs +33 -68
  48. package/src/RawEvent.res +7 -2
  49. package/src/RawEvent.res.mjs +4 -4
  50. package/src/RollbackCommit.res +4 -1
  51. package/src/RollbackCommit.res.mjs +3 -2
  52. package/src/SimulateItems.res +20 -7
  53. package/src/SimulateItems.res.mjs +7 -11
  54. package/src/TestIndexer.res +1 -1
  55. package/src/TestIndexer.res.mjs +1 -1
  56. package/src/sources/BlockStore.res +46 -0
  57. package/src/sources/BlockStore.res.mjs +24 -0
  58. package/src/sources/EventRouter.res +19 -12
  59. package/src/sources/EventRouter.res.mjs +7 -5
  60. package/src/sources/Evm.res +58 -5
  61. package/src/sources/Evm.res.mjs +44 -5
  62. package/src/sources/EvmChain.res +14 -18
  63. package/src/sources/EvmChain.res.mjs +12 -13
  64. package/src/sources/FieldMask.res +39 -0
  65. package/src/sources/FieldMask.res.mjs +42 -0
  66. package/src/sources/Fuel.res +8 -3
  67. package/src/sources/Fuel.res.mjs +5 -4
  68. package/src/sources/HyperFuelSource.res +31 -40
  69. package/src/sources/HyperFuelSource.res.mjs +52 -44
  70. package/src/sources/HyperSync.res +31 -9
  71. package/src/sources/HyperSync.res.mjs +47 -31
  72. package/src/sources/HyperSync.resi +10 -3
  73. package/src/sources/HyperSyncClient.res +15 -4
  74. package/src/sources/HyperSyncHeightStream.res +1 -8
  75. package/src/sources/HyperSyncHeightStream.res.mjs +0 -2
  76. package/src/sources/HyperSyncSource.res +58 -90
  77. package/src/sources/HyperSyncSource.res.mjs +57 -66
  78. package/src/sources/RpcSource.res +118 -120
  79. package/src/sources/RpcSource.res.mjs +92 -74
  80. package/src/sources/RpcWebSocketHeightStream.res +0 -5
  81. package/src/sources/RpcWebSocketHeightStream.res.mjs +0 -2
  82. package/src/sources/SimulateSource.res +5 -3
  83. package/src/sources/SimulateSource.res.mjs +12 -4
  84. package/src/sources/Source.res +19 -5
  85. package/src/sources/SourceManager.res +60 -10
  86. package/src/sources/SourceManager.res.mjs +54 -8
  87. package/src/sources/SourceManager.resi +10 -0
  88. package/src/sources/Svm.res +14 -10
  89. package/src/sources/Svm.res.mjs +23 -6
  90. package/src/sources/SvmHyperSyncClient.res +5 -6
  91. package/src/sources/SvmHyperSyncSource.res +82 -41
  92. package/src/sources/SvmHyperSyncSource.res.mjs +86 -41
  93. package/src/sources/TransactionStore.res +6 -107
  94. package/src/sources/TransactionStore.res.mjs +5 -86
  95. package/svm.schema.json +19 -0
@@ -0,0 +1,53 @@
1
+ // The single process-wide mutable state record, stashed on `globalThis` so a
2
+ // duplicate envio module instance — e.g. when the CLI's `bin.mjs` resolves
3
+ // envio from one path but the user's handlers resolve it from
4
+ // `node_modules/envio` — shares one state. Without this, each copy keeps its
5
+ // own module-level state: registration would read an empty registry and the
6
+ // `indexer.chains` getters would silently fall back to static config values.
7
+ //
8
+ // Slots are opaque (`unknown`) so this module stays at the bottom of the
9
+ // dependency graph; each owning module casts its slot to the real type once
10
+ // (`HandlerRegister` for the registration slots, `Main` for the runtime
11
+ // slots, `RollbackCommit` for its callbacks).
12
+ //
13
+ // Version-gated: the slot shapes can evolve between envio versions, so the
14
+ // guard uses strict full-version equality. On mismatch we throw with a
15
+ // deduplication hint instead of silently mixing shapes across builds.
16
+ type t = {
17
+ version: string,
18
+ eventRegistrations: dict<unknown>,
19
+ mutable activeRegistration: option<unknown>,
20
+ preRegistered: array<unknown>,
21
+ rollbackCommitCallbacks: array<unknown>,
22
+ mutable indexerState: option<unknown>,
23
+ mutable persistence: option<unknown>,
24
+ }
25
+
26
+ // Record type with `mutable` so assignment typechecks; ReScript keeps the
27
+ // field name verbatim in the generated JS so the globalThis slot is
28
+ // `__envioGlobal`.
29
+ type globalThis = {mutable __envioGlobal: Nullable.t<t>}
30
+ @val external globalThis: globalThis = "globalThis"
31
+
32
+ let value: t = {
33
+ let version = Utils.EnvioPackage.value.version
34
+ switch globalThis.__envioGlobal->Nullable.toOption {
35
+ | Some(existing) if existing.version === version => existing
36
+ | Some(existing) =>
37
+ JsError.throwWithMessage(
38
+ `Multiple incompatible envio versions loaded in the same process: ${existing.version} and ${version}. Deduplicate the 'envio' dependency in your project.`,
39
+ )
40
+ | None =>
41
+ let fresh = {
42
+ version,
43
+ eventRegistrations: Dict.make(),
44
+ activeRegistration: None,
45
+ preRegistered: [],
46
+ rollbackCommitCallbacks: [],
47
+ indexerState: None,
48
+ persistence: None,
49
+ }
50
+ globalThis.__envioGlobal = Nullable.make(fresh)
51
+ fresh
52
+ }
53
+ }
@@ -0,0 +1,31 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Utils from "./Utils.res.mjs";
4
+ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
5
+
6
+ let version = Utils.EnvioPackage.value.version;
7
+
8
+ let existing = globalThis.__envioGlobal;
9
+
10
+ let value;
11
+
12
+ if (existing == null) {
13
+ let fresh = {
14
+ version: version,
15
+ eventRegistrations: {},
16
+ activeRegistration: undefined,
17
+ preRegistered: [],
18
+ rollbackCommitCallbacks: [],
19
+ indexerState: undefined,
20
+ persistence: undefined
21
+ };
22
+ globalThis.__envioGlobal = fresh;
23
+ value = fresh;
24
+ } else {
25
+ value = existing.version === version ? existing : Stdlib_JsError.throwWithMessage(`Multiple incompatible envio versions loaded in the same process: ` + existing.version + ` and ` + version + `. Deduplicate the 'envio' dependency in your project.`);
26
+ }
27
+
28
+ export {
29
+ value,
30
+ }
31
+ /* existing Not a pure module */
@@ -243,9 +243,13 @@ let getTopicEncoder = (abiType: string): (unknown => EvmTypes.Hex.t) => {
243
243
  } else {
244
244
  switch abiType {
245
245
  | "address" =>
246
- TopicFilter.fromAddress->(
247
- Utils.magic: (Address.t => EvmTypes.Hex.t) => unknown => EvmTypes.Hex.t
248
- )
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)
249
253
 
250
254
  | "bool" =>
251
255
  TopicFilter.fromBool->(Utils.magic: (bool => EvmTypes.Hex.t) => unknown => EvmTypes.Hex.t)
@@ -378,46 +382,12 @@ let buildEvmEventConfig = (
378
382
  ~eventName: string,
379
383
  ~sighash: string,
380
384
  ~params: array<paramMeta>,
381
- ~isWildcard: bool,
382
- ~handler: option<Internal.handler>,
383
- ~contractRegister: option<Internal.contractRegister>,
384
- ~eventFilters: option<JSON.t>,
385
- ~probeChainId: int,
386
- ~onEventBlockFilterSchema: S.t<option<unknown>>,
387
385
  ~blockFields: option<array<Internal.evmBlockField>>=?,
388
386
  ~transactionFields: option<array<Internal.evmTransactionField>>=?,
389
- ~startBlock: option<int>=?,
390
387
  ~globalBlockFieldsSet: Utils.Set.t<Internal.evmBlockField>=Utils.Set.make(),
391
388
  ~globalTransactionFieldsSet: Utils.Set.t<Internal.evmTransactionField>=Utils.Set.make(),
392
389
  ): Internal.evmEventConfig => {
393
390
  let topicCount = params->Array.reduce(1, (acc, p) => p.indexed ? acc + 1 : acc)
394
- let indexedParams = params->Array.filter(p => p.indexed)
395
-
396
- let {
397
- getEventFiltersOrThrow,
398
- filterByAddresses,
399
- addressFilterParamGroups,
400
- startBlock: whereStartBlock,
401
- } = LogSelection.parseEventFiltersOrThrow(
402
- ~eventFilters,
403
- ~sighash,
404
- ~params=indexedParams->Array.map(p => p.name),
405
- ~contractName,
406
- ~probeChainId,
407
- ~onEventBlockFilterSchema,
408
- ~topic1=?indexedParams->Array.get(0)->Option.map(buildTopicGetter),
409
- ~topic2=?indexedParams->Array.get(1)->Option.map(buildTopicGetter),
410
- ~topic3=?indexedParams->Array.get(2)->Option.map(buildTopicGetter),
411
- )
412
-
413
- // `where.block.number._gte` overrides the contract-level startBlock
414
- // when present. The user opts into this explicitly in handler code so
415
- // it should win over the `config.yaml` contract `start_block`; absent
416
- // `where.block`, the caller's contract-level value passes through.
417
- let resolvedStartBlock = switch whereStartBlock {
418
- | Some(_) as sb => sb
419
- | None => startBlock
420
- }
421
391
 
422
392
  let (selectedBlockFields, selectedTransactionFields) = resolveFieldSelection(
423
393
  ~blockFields,
@@ -430,26 +400,75 @@ let buildEvmEventConfig = (
430
400
  id: sighash ++ "_" ++ topicCount->Int.toString,
431
401
  name: eventName,
432
402
  contractName,
433
- isWildcard,
434
- handler,
435
- contractRegister,
436
403
  paramsRawEventSchema: buildParamsSchema(params),
437
404
  simulateParamsSchema: buildSimulateParamsSchema(params),
438
- getEventFiltersOrThrow,
439
- filterByAddresses,
440
- clientAddressFilter: ?buildAddressFilter(addressFilterParamGroups, ~isWildcard),
441
- dependsOnAddresses: !isWildcard || filterByAddresses,
442
- startBlock: resolvedStartBlock,
443
405
  selectedBlockFields,
444
406
  selectedTransactionFields,
445
407
  transactionFieldMask: Evm.eventTransactionFieldMask(selectedTransactionFields),
408
+ blockFieldMask: Evm.eventBlockFieldMask(
409
+ selectedBlockFields->(
410
+ Utils.magic: Utils.Set.t<Internal.evmBlockField> => Utils.Set.t<string>
411
+ ),
412
+ ),
446
413
  sighash,
447
414
  topicCount,
448
415
  paramsMetadata: params,
449
416
  }
450
417
  }
451
418
 
452
- // ============== Build Fuel event config ==============
419
+ // Enrich an EVM definition into a per-(event,chain) registration: resolve the
420
+ // registered `where` for this chain into `resolvedWhere` + address filters,
421
+ // and override `startBlock` with `where.block._gte`.
422
+ let buildEvmOnEventRegistration = (
423
+ ~eventConfig: Internal.evmEventConfig,
424
+ ~isWildcard: bool,
425
+ ~handler: option<Internal.handler>,
426
+ ~contractRegister: option<Internal.contractRegister>,
427
+ ~where: option<JSON.t>,
428
+ ~chainId: int,
429
+ ~onEventBlockFilterSchema: S.t<option<unknown>>,
430
+ ~startBlock: option<int>=?,
431
+ ): Internal.evmOnEventRegistration => {
432
+ let indexedParams = eventConfig.paramsMetadata->Array.filter(p => p.indexed)
433
+
434
+ let {resolvedWhere, filterByAddresses, addressFilterParamGroups} = LogSelection.parseWhereOrThrow(
435
+ ~where,
436
+ ~sighash=eventConfig.sighash,
437
+ ~params=indexedParams->Array.map(p => p.name),
438
+ ~contractName=eventConfig.contractName,
439
+ ~chainId,
440
+ ~onEventBlockFilterSchema,
441
+ ~topic1=?indexedParams->Array.get(0)->Option.map(buildTopicGetter),
442
+ ~topic2=?indexedParams->Array.get(1)->Option.map(buildTopicGetter),
443
+ ~topic3=?indexedParams->Array.get(2)->Option.map(buildTopicGetter),
444
+ )
445
+
446
+ // `where.block.number._gte` overrides the contract-level startBlock when
447
+ // present (an explicit per-event opt-in that wins over `config.yaml`);
448
+ // otherwise the contract/chain value passes through.
449
+ let resolvedStartBlock = switch resolvedWhere.startBlock {
450
+ | Some(_) as sb => sb
451
+ | None => startBlock
452
+ }
453
+
454
+ {
455
+ eventConfig: (eventConfig :> Internal.eventConfig),
456
+ isWildcard,
457
+ handler,
458
+ contractRegister,
459
+ resolvedWhere,
460
+ filterByAddresses,
461
+ clientAddressFilter: ?buildAddressFilter(addressFilterParamGroups, ~isWildcard),
462
+ dependsOnAddresses: Internal.dependsOnAddresses(~isWildcard, ~filterByAddresses),
463
+ startBlock: resolvedStartBlock,
464
+ }
465
+ }
466
+
467
+ // ============== Build SVM instruction event config ==============
468
+
469
+ // Always-included block fields (slot, time, hash) are prepended at runtime so
470
+ // they're always present regardless of config.
471
+ let alwaysIncludedSvmBlockFields: array<Internal.svmBlockField> = [Slot, Time, Hash]
453
472
 
454
473
  let buildSvmInstructionEventConfig = (
455
474
  ~contractName: string,
@@ -459,15 +478,12 @@ let buildSvmInstructionEventConfig = (
459
478
  ~discriminatorByteLen: int,
460
479
  ~includeLogs: bool,
461
480
  ~transactionFields: array<Internal.svmTransactionField>=[],
481
+ ~blockFields: array<Internal.svmBlockField>=[],
462
482
  ~accountFilters: array<Internal.svmAccountFilterGroup>,
463
483
  ~isInner: option<bool>,
464
- ~isWildcard: bool,
465
- ~handler: option<Internal.handler>,
466
- ~contractRegister: option<Internal.contractRegister>,
467
484
  ~accounts: array<string>=[],
468
485
  ~args: JSON.t=JSON.Null,
469
486
  ~definedTypes: JSON.t=JSON.Null,
470
- ~startBlock: option<int>=?,
471
487
  ): Internal.svmInstructionEventConfig => {
472
488
  let paramsSchema =
473
489
  S.json(~validate=false)
@@ -480,6 +496,12 @@ let buildSvmInstructionEventConfig = (
480
496
  Utils.Set.fromArray(transactionFields)->(
481
497
  Utils.magic: Utils.Set.t<Internal.svmTransactionField> => Utils.Set.t<string>
482
498
  )
499
+ let selectedBlockFields = Utils.Set.fromArray(
500
+ Array.concat(alwaysIncludedSvmBlockFields, blockFields),
501
+ )
502
+ let blockFieldMask = Svm.eventBlockFieldMask(
503
+ selectedBlockFields->(Utils.magic: Utils.Set.t<Internal.svmBlockField> => Utils.Set.t<string>),
504
+ )
483
505
  {
484
506
  id: switch discriminator {
485
507
  | Some(d) => d
@@ -487,21 +509,16 @@ let buildSvmInstructionEventConfig = (
487
509
  },
488
510
  name: instructionName,
489
511
  contractName,
490
- isWildcard,
491
- handler,
492
- contractRegister,
493
512
  paramsRawEventSchema: paramsSchema,
494
513
  simulateParamsSchema: paramsSchema,
495
- filterByAddresses: false,
496
- dependsOnAddresses: !isWildcard,
497
- clientAddressFilter: ?buildAddressFilter([], ~isWildcard, ~srcAddressExpr="event.programId"),
498
- startBlock,
499
514
  programId,
500
515
  discriminator,
501
516
  discriminatorByteLen,
502
517
  includeLogs,
503
518
  selectedTransactionFields,
504
519
  transactionFieldMask: Svm.eventTransactionFieldMask(selectedTransactionFields),
520
+ selectedBlockFields,
521
+ blockFieldMask,
505
522
  accountFilters,
506
523
  isInner,
507
524
  accounts,
@@ -510,16 +527,33 @@ let buildSvmInstructionEventConfig = (
510
527
  }
511
528
  }
512
529
 
530
+ // Enrich an SVM definition into a registration. SVM has no `where`; only the
531
+ // handler binding + wildcard-derived address gate are registration state.
532
+ let buildSvmOnEventRegistration = (
533
+ ~eventConfig: Internal.svmInstructionEventConfig,
534
+ ~isWildcard: bool,
535
+ ~handler: option<Internal.handler>,
536
+ ~contractRegister: option<Internal.contractRegister>,
537
+ ~startBlock: option<int>=?,
538
+ ): Internal.svmOnEventRegistration => {
539
+ eventConfig: (eventConfig :> Internal.eventConfig),
540
+ handler,
541
+ contractRegister,
542
+ isWildcard,
543
+ filterByAddresses: false,
544
+ dependsOnAddresses: Internal.dependsOnAddresses(~isWildcard, ~filterByAddresses=false),
545
+ clientAddressFilter: ?buildAddressFilter([], ~isWildcard, ~srcAddressExpr="event.programId"),
546
+ startBlock,
547
+ }
548
+
549
+ // ============== Build Fuel event config ==============
550
+
513
551
  let buildFuelEventConfig = (
514
552
  ~contractName: string,
515
553
  ~eventName: string,
516
554
  ~kind: string,
517
555
  ~sighash: string,
518
556
  ~rawAbi: JSON.t,
519
- ~isWildcard: bool,
520
- ~handler: option<Internal.handler>,
521
- ~contractRegister: option<Internal.contractRegister>,
522
- ~startBlock: option<int>=?,
523
557
  ): Internal.fuelEventConfig => {
524
558
  let fuelKind = switch kind {
525
559
  | "logData" =>
@@ -559,18 +593,32 @@ let buildFuelEventConfig = (
559
593
  },
560
594
  name: eventName,
561
595
  contractName,
562
- isWildcard,
563
- handler,
564
- contractRegister,
565
596
  paramsRawEventSchema: paramsSchema,
566
597
  simulateParamsSchema: paramsSchema,
567
- filterByAddresses: false,
568
- dependsOnAddresses: !isWildcard,
569
- clientAddressFilter: ?buildAddressFilter([], ~isWildcard),
570
- startBlock,
571
- // Fuel keeps the transaction inline on the payload; nothing to materialise.
598
+ // Fuel keeps the transaction and block inline on the payload; nothing to
599
+ // materialise.
572
600
  selectedTransactionFields: Utils.Set.make(),
573
601
  transactionFieldMask: 0.,
602
+ blockFieldMask: 0.,
574
603
  kind: fuelKind,
575
604
  }
576
605
  }
606
+
607
+ // Enrich a Fuel definition into a registration (handler binding +
608
+ // wildcard-derived address gate; Fuel never filters by addresses).
609
+ let buildFuelOnEventRegistration = (
610
+ ~eventConfig: Internal.fuelEventConfig,
611
+ ~isWildcard: bool,
612
+ ~handler: option<Internal.handler>,
613
+ ~contractRegister: option<Internal.contractRegister>,
614
+ ~startBlock: option<int>=?,
615
+ ): Internal.fuelOnEventRegistration => {
616
+ eventConfig: (eventConfig :> Internal.eventConfig),
617
+ handler,
618
+ contractRegister,
619
+ isWildcard,
620
+ filterByAddresses: false,
621
+ dependsOnAddresses: Internal.dependsOnAddresses(~isWildcard, ~filterByAddresses=false),
622
+ clientAddressFilter: ?buildAddressFilter([], ~isWildcard),
623
+ startBlock,
624
+ }
@@ -213,7 +213,7 @@ function getTopicEncoder(abiType) {
213
213
  }
214
214
  switch (abiType) {
215
215
  case "address" :
216
- return TopicFilter.fromAddress;
216
+ return value => TopicFilter.fromAddress(value.toLowerCase());
217
217
  case "bool" :
218
218
  return TopicFilter.fromBool;
219
219
  case "bytes" :
@@ -280,7 +280,7 @@ function buildAddressFilter(groups, isWildcard, srcAddressExprOpt) {
280
280
  return Stdlib_Option.map(buildAddressFilterBody(groups, isWildcard, srcAddressExpr), compileAddressFilter);
281
281
  }
282
282
 
283
- function buildEvmEventConfig(contractName, eventName, sighash, params, isWildcard, handler, contractRegister, eventFilters, probeChainId, onEventBlockFilterSchema, blockFields, transactionFields, startBlock, globalBlockFieldsSetOpt, globalTransactionFieldsSetOpt) {
283
+ function buildEvmEventConfig(contractName, eventName, sighash, params, blockFields, transactionFields, globalBlockFieldsSetOpt, globalTransactionFieldsSetOpt) {
284
284
  let globalBlockFieldsSet = globalBlockFieldsSetOpt !== undefined ? Primitive_option.valFromOption(globalBlockFieldsSetOpt) : new Set();
285
285
  let globalTransactionFieldsSet = globalTransactionFieldsSetOpt !== undefined ? Primitive_option.valFromOption(globalTransactionFieldsSetOpt) : new Set();
286
286
  let topicCount = Stdlib_Array.reduce(params, 1, (acc, p) => {
@@ -290,58 +290,71 @@ function buildEvmEventConfig(contractName, eventName, sighash, params, isWildcar
290
290
  return acc;
291
291
  }
292
292
  });
293
- let indexedParams = params.filter(p => p.indexed);
294
- let match = LogSelection.parseEventFiltersOrThrow(eventFilters, sighash, indexedParams.map(p => p.name), contractName, probeChainId, onEventBlockFilterSchema, Stdlib_Option.map(indexedParams[0], buildTopicGetter), Stdlib_Option.map(indexedParams[1], buildTopicGetter), Stdlib_Option.map(indexedParams[2], buildTopicGetter));
295
- let whereStartBlock = match.startBlock;
296
- let filterByAddresses = match.filterByAddresses;
297
- let resolvedStartBlock = whereStartBlock !== undefined ? whereStartBlock : startBlock;
298
- let match$1 = resolveFieldSelection(blockFields, transactionFields, globalBlockFieldsSet, globalTransactionFieldsSet);
299
- let selectedTransactionFields = match$1[1];
293
+ let match = resolveFieldSelection(blockFields, transactionFields, globalBlockFieldsSet, globalTransactionFieldsSet);
294
+ let selectedTransactionFields = match[1];
295
+ let selectedBlockFields = match[0];
300
296
  return {
301
297
  id: sighash + "_" + topicCount.toString(),
302
298
  name: eventName,
303
299
  contractName: contractName,
304
- isWildcard: isWildcard,
305
- filterByAddresses: filterByAddresses,
306
- dependsOnAddresses: !isWildcard || filterByAddresses,
307
- clientAddressFilter: buildAddressFilter(match.addressFilterParamGroups, isWildcard, undefined),
308
- handler: handler,
309
- contractRegister: contractRegister,
310
300
  paramsRawEventSchema: buildParamsSchema(params),
311
301
  simulateParamsSchema: buildSimulateParamsSchema(params),
312
- startBlock: resolvedStartBlock,
313
302
  selectedTransactionFields: selectedTransactionFields,
314
303
  transactionFieldMask: Evm.eventTransactionFieldMask(selectedTransactionFields),
315
- getEventFiltersOrThrow: match.getEventFiltersOrThrow,
316
- selectedBlockFields: match$1[0],
304
+ blockFieldMask: Evm.eventBlockFieldMask(selectedBlockFields),
305
+ selectedBlockFields: selectedBlockFields,
317
306
  sighash: sighash,
318
307
  topicCount: topicCount,
319
308
  paramsMetadata: params
320
309
  };
321
310
  }
322
311
 
323
- function buildSvmInstructionEventConfig(contractName, instructionName, programId, discriminator, discriminatorByteLen, includeLogs, transactionFieldsOpt, accountFilters, isInner, isWildcard, handler, contractRegister, accountsOpt, argsOpt, definedTypesOpt, startBlock) {
312
+ function buildEvmOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, where, chainId, onEventBlockFilterSchema, startBlock) {
313
+ let indexedParams = eventConfig.paramsMetadata.filter(p => p.indexed);
314
+ let match = LogSelection.parseWhereOrThrow(where, eventConfig.sighash, indexedParams.map(p => p.name), eventConfig.contractName, chainId, onEventBlockFilterSchema, Stdlib_Option.map(indexedParams[0], buildTopicGetter), Stdlib_Option.map(indexedParams[1], buildTopicGetter), Stdlib_Option.map(indexedParams[2], buildTopicGetter));
315
+ let filterByAddresses = match.filterByAddresses;
316
+ let resolvedWhere = match.resolvedWhere;
317
+ let sb = resolvedWhere.startBlock;
318
+ let resolvedStartBlock = sb !== undefined ? sb : startBlock;
319
+ return {
320
+ eventConfig: eventConfig,
321
+ handler: handler,
322
+ contractRegister: contractRegister,
323
+ isWildcard: isWildcard,
324
+ filterByAddresses: filterByAddresses,
325
+ dependsOnAddresses: Internal.dependsOnAddresses(isWildcard, filterByAddresses),
326
+ clientAddressFilter: buildAddressFilter(match.addressFilterParamGroups, isWildcard, undefined),
327
+ startBlock: resolvedStartBlock,
328
+ resolvedWhere: resolvedWhere
329
+ };
330
+ }
331
+
332
+ let alwaysIncludedSvmBlockFields = [
333
+ "slot",
334
+ "time",
335
+ "hash"
336
+ ];
337
+
338
+ function buildSvmInstructionEventConfig(contractName, instructionName, programId, discriminator, discriminatorByteLen, includeLogs, transactionFieldsOpt, blockFieldsOpt, accountFilters, isInner, accountsOpt, argsOpt, definedTypesOpt) {
324
339
  let transactionFields = transactionFieldsOpt !== undefined ? transactionFieldsOpt : [];
340
+ let blockFields = blockFieldsOpt !== undefined ? blockFieldsOpt : [];
325
341
  let accounts = accountsOpt !== undefined ? accountsOpt : [];
326
342
  let args = argsOpt !== undefined ? argsOpt : null;
327
343
  let definedTypes = definedTypesOpt !== undefined ? definedTypesOpt : null;
328
344
  let paramsSchema = Utils.Schema.coerceToJsonPgType(S$RescriptSchema.json(false));
329
345
  let selectedTransactionFields = new Set(transactionFields);
346
+ let selectedBlockFields = new Set(alwaysIncludedSvmBlockFields.concat(blockFields));
347
+ let blockFieldMask = Svm.eventBlockFieldMask(selectedBlockFields);
330
348
  return {
331
349
  id: discriminator !== undefined ? discriminator : "none",
332
350
  name: instructionName,
333
351
  contractName: contractName,
334
- isWildcard: isWildcard,
335
- filterByAddresses: false,
336
- dependsOnAddresses: !isWildcard,
337
- clientAddressFilter: buildAddressFilter([], isWildcard, "event.programId"),
338
- handler: handler,
339
- contractRegister: contractRegister,
340
352
  paramsRawEventSchema: paramsSchema,
341
353
  simulateParamsSchema: paramsSchema,
342
- startBlock: startBlock,
343
354
  selectedTransactionFields: selectedTransactionFields,
344
355
  transactionFieldMask: Svm.eventTransactionFieldMask(selectedTransactionFields),
356
+ blockFieldMask: blockFieldMask,
357
+ selectedBlockFields: selectedBlockFields,
345
358
  programId: programId,
346
359
  discriminator: discriminator,
347
360
  discriminatorByteLen: discriminatorByteLen,
@@ -354,7 +367,20 @@ function buildSvmInstructionEventConfig(contractName, instructionName, programId
354
367
  };
355
368
  }
356
369
 
357
- function buildFuelEventConfig(contractName, eventName, kind, sighash, rawAbi, isWildcard, handler, contractRegister, startBlock) {
370
+ function buildSvmOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, startBlock) {
371
+ return {
372
+ eventConfig: eventConfig,
373
+ handler: handler,
374
+ contractRegister: contractRegister,
375
+ isWildcard: isWildcard,
376
+ filterByAddresses: false,
377
+ dependsOnAddresses: Internal.dependsOnAddresses(isWildcard, false),
378
+ clientAddressFilter: buildAddressFilter([], isWildcard, "event.programId"),
379
+ startBlock: startBlock
380
+ };
381
+ }
382
+
383
+ function buildFuelEventConfig(contractName, eventName, kind, sighash, rawAbi) {
358
384
  let fuelKind;
359
385
  switch (kind) {
360
386
  case "burn" :
@@ -401,21 +427,28 @@ function buildFuelEventConfig(contractName, eventName, kind, sighash, rawAbi, is
401
427
  id: tmp,
402
428
  name: eventName,
403
429
  contractName: contractName,
404
- isWildcard: isWildcard,
405
- filterByAddresses: false,
406
- dependsOnAddresses: !isWildcard,
407
- clientAddressFilter: buildAddressFilter([], isWildcard, undefined),
408
- handler: handler,
409
- contractRegister: contractRegister,
410
430
  paramsRawEventSchema: paramsSchema,
411
431
  simulateParamsSchema: paramsSchema,
412
- startBlock: startBlock,
413
432
  selectedTransactionFields: new Set(),
414
433
  transactionFieldMask: 0,
434
+ blockFieldMask: 0,
415
435
  kind: fuelKind
416
436
  };
417
437
  }
418
438
 
439
+ function buildFuelOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, startBlock) {
440
+ return {
441
+ eventConfig: eventConfig,
442
+ handler: handler,
443
+ contractRegister: contractRegister,
444
+ isWildcard: isWildcard,
445
+ filterByAddresses: false,
446
+ dependsOnAddresses: Internal.dependsOnAddresses(isWildcard, false),
447
+ clientAddressFilter: buildAddressFilter([], isWildcard, undefined),
448
+ startBlock: startBlock
449
+ };
450
+ }
451
+
419
452
  export {
420
453
  paramMetaSchema,
421
454
  normalizeOrThrow,
@@ -435,7 +468,11 @@ export {
435
468
  buildAddressFilterBody,
436
469
  buildAddressFilter,
437
470
  buildEvmEventConfig,
471
+ buildEvmOnEventRegistration,
472
+ alwaysIncludedSvmBlockFields,
438
473
  buildSvmInstructionEventConfig,
474
+ buildSvmOnEventRegistration,
439
475
  buildFuelEventConfig,
476
+ buildFuelOnEventRegistration,
440
477
  }
441
478
  /* paramMetaSchema Not a pure module */
@@ -74,8 +74,8 @@ let runEventHandlerOrThrow = async (
74
74
  }
75
75
  let handlerDuration = timeBeforeHandler->Performance.secondsSince
76
76
  Prometheus.ProcessingHandler.increment(
77
- ~contract=eventItem.eventConfig.contractName,
78
- ~event=eventItem.eventConfig.name,
77
+ ~contract=eventItem.onEventRegistration.eventConfig.contractName,
78
+ ~event=eventItem.onEventRegistration.eventConfig.name,
79
79
  ~duration=handlerDuration,
80
80
  )
81
81
  }
@@ -90,7 +90,7 @@ let runHandlerOrThrow = async (
90
90
  ~chains: Internal.chains,
91
91
  ) => {
92
92
  switch item {
93
- | Block({onBlockConfig: {handler}, blockNumber}) =>
93
+ | Block({onBlockRegistration: {handler}, blockNumber}) =>
94
94
  try {
95
95
  let contextParams: UserContext.contextParams = {
96
96
  item,
@@ -121,8 +121,8 @@ let runHandlerOrThrow = async (
121
121
  }),
122
122
  )
123
123
  }
124
- | Event({eventConfig}) =>
125
- switch eventConfig.handler {
124
+ | Event({onEventRegistration}) =>
125
+ switch onEventRegistration.handler {
126
126
  | Some(handler) =>
127
127
  await item->runEventHandlerOrThrow(
128
128
  ~handler,
@@ -161,7 +161,7 @@ let preloadBatchOrThrow = async (
161
161
  for idx in 0 to checkpointEventsProcessed - 1 {
162
162
  let item = batch.items->Array.getUnsafe(itemIdx.contents + idx)
163
163
  switch item {
164
- | Event({eventConfig: {handler, contractName, name: eventName}}) =>
164
+ | Event({onEventRegistration: {handler, eventConfig: {contractName, name: eventName}}}) =>
165
165
  switch handler {
166
166
  | None => ()
167
167
  | Some(handler) =>
@@ -200,7 +200,7 @@ let preloadBatchOrThrow = async (
200
200
  | _ => ()
201
201
  }
202
202
  }
203
- | Block({onBlockConfig: {handler}, blockNumber}) =>
203
+ | Block({onBlockRegistration: {handler}, blockNumber}) =>
204
204
  try {
205
205
  promises->Array.push(
206
206
  handler({
@@ -289,15 +289,19 @@ type logPartitionInfo = {
289
289
  lastItemBlockNumber?: int,
290
290
  }
291
291
 
292
- // Off the hot path: bulk-materialise the selected transaction fields for the
293
- // batch's store-backed (HyperSync) items and write them onto the payloads, so
294
- // handlers read plain objects. A batch can span chains, each with its own store
295
- // and field mask, so group items by chain before materialising.
296
- let materializeBatchTransactions = async (batch: Batch.t, ~chainStates: dict<ChainState.t>) => {
292
+ // Off the hot path: bulk-materialise the selected transaction and block fields
293
+ // for the batch's store-backed (HyperSync) items and write them onto the
294
+ // payloads, so handlers read plain objects. A batch can span chains, each with
295
+ // its own stores and field masks, so group items by chain before materialising.
296
+ let materializeBatchEvents = async (
297
+ batch: Batch.t,
298
+ ~chainStates: dict<ChainState.t>,
299
+ ~ecosystem,
300
+ ) => {
297
301
  switch chainStates->Dict.valuesToArray {
298
302
  // Single-chain indexers (the common case): every item belongs to the one
299
303
  // chain, so skip the per-chain grouping and its allocations.
300
- | [cs] => await cs->ChainState.materializeBatchItems(~items=batch.items)
304
+ | [cs] => await cs->ChainState.materializeBatchItems(~items=batch.items, ~ecosystem)
301
305
  | _ =>
302
306
  let itemsByChain: dict<array<Internal.item>> = Dict.make()
303
307
  batch.items->Array.forEach(item => {
@@ -312,7 +316,7 @@ let materializeBatchTransactions = async (batch: Batch.t, ~chainStates: dict<Cha
312
316
  ->Dict.toArray
313
317
  ->Array.map(async ((chainId, items)) => {
314
318
  let cs = chainStates->Dict.getUnsafe(chainId)
315
- await cs->ChainState.materializeBatchItems(~items)
319
+ await cs->ChainState.materializeBatchItems(~items, ~ecosystem)
316
320
  })
317
321
  ->Promise.all
318
322
  }
@@ -348,7 +352,7 @@ let processEventBatch = async (
348
352
  if batch.items->Utils.Array.notEmpty {
349
353
  // Materialise store-backed transactions onto payloads before any handler
350
354
  // (preload or execute) reads them.
351
- await materializeBatchTransactions(batch, ~chainStates)
355
+ await materializeBatchEvents(batch, ~chainStates, ~ecosystem=config.ecosystem.name)
352
356
  await batch->preloadBatchOrThrow(~loadManager, ~persistence, ~indexerState, ~chains, ~config)
353
357
  }
354
358