envio 3.3.0-alpha.3 → 3.3.0-alpha.4

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.
@@ -14,7 +14,7 @@ function deriveEffectiveStartBlock(registrationBlock, contractStartBlock) {
14
14
  return Primitive_int.max(Primitive_int.max(registrationBlock, 0), Stdlib_Option.getOr(contractStartBlock, 0));
15
15
  }
16
16
 
17
- function deriveContractNameByAddress(addressesByContractName) {
17
+ let deriveContractNameByAddress = Utils.$$WeakMap.memoize(addressesByContractName => {
18
18
  let result = {};
19
19
  Utils.Dict.forEachWithKey(addressesByContractName, (addresses, contractName) => {
20
20
  for (let i = 0, i_finish = addresses.length; i < i_finish; ++i) {
@@ -22,7 +22,7 @@ function deriveContractNameByAddress(addressesByContractName) {
22
22
  }
23
23
  });
24
24
  return result;
25
- }
25
+ });
26
26
 
27
27
  function calculateEstResponseSize(p, fromBlock, toBlock, maxQueryBlockNumber) {
28
28
  if (p.prevQueryRange <= 0) {
@@ -82,7 +82,6 @@ function mergePartitionsAtBlock(p1, p2, potentialMergeBlock, contractName, maxAd
82
82
  latestFetchedBlock: p1.latestFetchedBlock,
83
83
  selection: p1.selection,
84
84
  addressesByContractName: p1.addressesByContractName,
85
- contractNameByAddress: p1.contractNameByAddress,
86
85
  mergeBlock: potentialMergeBlock,
87
86
  dynamicContract: p1.dynamicContract,
88
87
  mutPendingQueries: p1.mutPendingQueries,
@@ -96,7 +95,6 @@ function mergePartitionsAtBlock(p1, p2, potentialMergeBlock, contractName, maxAd
96
95
  latestFetchedBlock: p2.latestFetchedBlock,
97
96
  selection: p2.selection,
98
97
  addressesByContractName: p2.addressesByContractName,
99
- contractNameByAddress: p2.contractNameByAddress,
100
98
  mergeBlock: potentialMergeBlock,
101
99
  dynamicContract: p2.dynamicContract,
102
100
  mutPendingQueries: p2.mutPendingQueries,
@@ -119,7 +117,6 @@ function mergePartitionsAtBlock(p1, p2, potentialMergeBlock, contractName, maxAd
119
117
  },
120
118
  selection: p1.selection,
121
119
  addressesByContractName: {},
122
- contractNameByAddress: {},
123
120
  mergeBlock: undefined,
124
121
  dynamicContract: contractName,
125
122
  mutPendingQueries: [],
@@ -134,7 +131,6 @@ function mergePartitionsAtBlock(p1, p2, potentialMergeBlock, contractName, maxAd
134
131
  latestFetchedBlock: p1.latestFetchedBlock,
135
132
  selection: p1.selection,
136
133
  addressesByContractName: p1.addressesByContractName,
137
- contractNameByAddress: p1.contractNameByAddress,
138
134
  mergeBlock: potentialMergeBlock,
139
135
  dynamicContract: p1.dynamicContract,
140
136
  mutPendingQueries: p1.mutPendingQueries,
@@ -151,7 +147,6 @@ function mergePartitionsAtBlock(p1, p2, potentialMergeBlock, contractName, maxAd
151
147
  latestFetchedBlock: p2.latestFetchedBlock,
152
148
  selection: p2.selection,
153
149
  addressesByContractName: p2.addressesByContractName,
154
- contractNameByAddress: p2.contractNameByAddress,
155
150
  mergeBlock: potentialMergeBlock,
156
151
  dynamicContract: p2.dynamicContract,
157
152
  mutPendingQueries: p2.mutPendingQueries,
@@ -176,7 +171,6 @@ function mergePartitionsAtBlock(p1, p2, potentialMergeBlock, contractName, maxAd
176
171
  latestFetchedBlock: continuingBase.latestFetchedBlock,
177
172
  selection: continuingBase.selection,
178
173
  addressesByContractName: abcFull,
179
- contractNameByAddress: continuingBase.contractNameByAddress,
180
174
  mergeBlock: continuingBase.mergeBlock,
181
175
  dynamicContract: continuingBase.dynamicContract,
182
176
  mutPendingQueries: continuingBase.mutPendingQueries,
@@ -192,7 +186,6 @@ function mergePartitionsAtBlock(p1, p2, potentialMergeBlock, contractName, maxAd
192
186
  latestFetchedBlock: continuingBase.latestFetchedBlock,
193
187
  selection: continuingBase.selection,
194
188
  addressesByContractName: abcRest,
195
- contractNameByAddress: continuingBase.contractNameByAddress,
196
189
  mergeBlock: continuingBase.mergeBlock,
197
190
  dynamicContract: continuingBase.dynamicContract,
198
191
  mutPendingQueries: [],
@@ -210,7 +203,6 @@ function mergePartitionsAtBlock(p1, p2, potentialMergeBlock, contractName, maxAd
210
203
  latestFetchedBlock: continuingBase.latestFetchedBlock,
211
204
  selection: continuingBase.selection,
212
205
  addressesByContractName: abc,
213
- contractNameByAddress: continuingBase.contractNameByAddress,
214
206
  mergeBlock: continuingBase.mergeBlock,
215
207
  dynamicContract: continuingBase.dynamicContract,
216
208
  mutPendingQueries: continuingBase.mutPendingQueries,
@@ -309,20 +301,7 @@ function make(partitions, maxAddrInPartition, nextPartitionIndex, dynamicContrac
309
301
  for (let idx$2 = 0; idx$2 < partitionsCount; ++idx$2) {
310
302
  let p$1 = newPartitions[idx$2];
311
303
  idsInAscOrder[idx$2] = p$1.id;
312
- entities[p$1.id] = {
313
- id: p$1.id,
314
- latestFetchedBlock: p$1.latestFetchedBlock,
315
- selection: p$1.selection,
316
- addressesByContractName: p$1.addressesByContractName,
317
- contractNameByAddress: deriveContractNameByAddress(p$1.addressesByContractName),
318
- mergeBlock: p$1.mergeBlock,
319
- dynamicContract: p$1.dynamicContract,
320
- mutPendingQueries: p$1.mutPendingQueries,
321
- prevQueryRange: p$1.prevQueryRange,
322
- prevPrevQueryRange: p$1.prevPrevQueryRange,
323
- prevRangeSize: p$1.prevRangeSize,
324
- latestBlockRangeUpdateBlock: p$1.latestBlockRangeUpdateBlock
325
- };
304
+ entities[p$1.id] = p$1;
326
305
  }
327
306
  return {
328
307
  idsInAscOrder: idsInAscOrder,
@@ -416,7 +395,6 @@ function handleQueryResponse(optimizedPartitions, query, knownHeight, itemsCount
416
395
  let updatedMainPartition_id = p$1.id;
417
396
  let updatedMainPartition_selection = p$1.selection;
418
397
  let updatedMainPartition_addressesByContractName = p$1.addressesByContractName;
419
- let updatedMainPartition_contractNameByAddress = p$1.contractNameByAddress;
420
398
  let updatedMainPartition_mergeBlock = p$1.mergeBlock;
421
399
  let updatedMainPartition_dynamicContract = p$1.dynamicContract;
422
400
  let updatedMainPartition_mutPendingQueries = p$1.mutPendingQueries;
@@ -426,7 +404,6 @@ function handleQueryResponse(optimizedPartitions, query, knownHeight, itemsCount
426
404
  latestFetchedBlock: updatedLatestFetchedBlock,
427
405
  selection: updatedMainPartition_selection,
428
406
  addressesByContractName: updatedMainPartition_addressesByContractName,
429
- contractNameByAddress: updatedMainPartition_contractNameByAddress,
430
407
  mergeBlock: updatedMainPartition_mergeBlock,
431
408
  dynamicContract: updatedMainPartition_dynamicContract,
432
409
  mutPendingQueries: updatedMainPartition_mutPendingQueries,
@@ -674,7 +651,6 @@ function createPartitionsFromIndexingAddresses(registeringContractsByContract, d
674
651
  latestFetchedBlock: latestFetchedBlock,
675
652
  selection: normalSelection,
676
653
  addressesByContractName: addressesByContractName,
677
- contractNameByAddress: {},
678
654
  mergeBlock: undefined,
679
655
  dynamicContract: isDynamic ? contractName : undefined,
680
656
  mutPendingQueries: [],
@@ -726,7 +702,6 @@ function createPartitionsFromIndexingAddresses(registeringContractsByContract, d
726
702
  latestFetchedBlock: currentP.latestFetchedBlock,
727
703
  selection: currentP.selection,
728
704
  addressesByContractName: currentP.addressesByContractName,
729
- contractNameByAddress: currentP.contractNameByAddress,
730
705
  mergeBlock: currentPBlock < nextPBlock ? nextPBlock : undefined,
731
706
  dynamicContract: currentP.dynamicContract,
732
707
  mutPendingQueries: currentP.mutPendingQueries,
@@ -740,7 +715,6 @@ function createPartitionsFromIndexingAddresses(registeringContractsByContract, d
740
715
  latestFetchedBlock: nextP.latestFetchedBlock,
741
716
  selection: nextP.selection,
742
717
  addressesByContractName: mergedAddresses,
743
- contractNameByAddress: nextP.contractNameByAddress,
744
718
  mergeBlock: nextP.mergeBlock,
745
719
  dynamicContract: nextP.dynamicContract,
746
720
  mutPendingQueries: nextP.mutPendingQueries,
@@ -755,7 +729,6 @@ function createPartitionsFromIndexingAddresses(registeringContractsByContract, d
755
729
  latestFetchedBlock: currentP.latestFetchedBlock,
756
730
  selection: currentP.selection,
757
731
  addressesByContractName: mergedAddresses,
758
- contractNameByAddress: currentP.contractNameByAddress,
759
732
  mergeBlock: currentP.mergeBlock,
760
733
  dynamicContract: currentP.dynamicContract,
761
734
  mutPendingQueries: currentP.mutPendingQueries,
@@ -901,7 +874,6 @@ function registerDynamicContracts(fetchState, items) {
901
874
  latestFetchedBlock: p.latestFetchedBlock,
902
875
  selection: p.selection,
903
876
  addressesByContractName: restAddressesByContractName,
904
- contractNameByAddress: p.contractNameByAddress,
905
877
  mergeBlock: p.mergeBlock,
906
878
  dynamicContract: p.dynamicContract,
907
879
  mutPendingQueries: p.mutPendingQueries,
@@ -917,7 +889,6 @@ function registerDynamicContracts(fetchState, items) {
917
889
  latestFetchedBlock: p.latestFetchedBlock,
918
890
  selection: fetchState.normalSelection,
919
891
  addressesByContractName: addressesByContractName,
920
- contractNameByAddress: {},
921
892
  mergeBlock: undefined,
922
893
  dynamicContract: contractName,
923
894
  mutPendingQueries: p.mutPendingQueries,
@@ -933,7 +904,6 @@ function registerDynamicContracts(fetchState, items) {
933
904
  latestFetchedBlock: p.latestFetchedBlock,
934
905
  selection: p.selection,
935
906
  addressesByContractName: p.addressesByContractName,
936
- contractNameByAddress: p.contractNameByAddress,
937
907
  mergeBlock: p.mergeBlock,
938
908
  dynamicContract: contractName,
939
909
  mutPendingQueries: p.mutPendingQueries,
@@ -1029,8 +999,7 @@ function pushQueriesForRange(queries, partitionId, rangeFromBlock, rangeEndBlock
1029
999
  chainId: 0,
1030
1000
  progress: 0,
1031
1001
  selection: selection,
1032
- addressesByContractName: addressesByContractName,
1033
- contractNameByAddress: partition.contractNameByAddress
1002
+ addressesByContractName: addressesByContractName
1034
1003
  });
1035
1004
  chunkFromBlock = chunkToBlock + 1 | 0;
1036
1005
  chunkIdx = chunkIdx + 1 | 0;
@@ -1046,8 +1015,7 @@ function pushQueriesForRange(queries, partitionId, rangeFromBlock, rangeEndBlock
1046
1015
  chainId: 0,
1047
1016
  progress: 0,
1048
1017
  selection: selection,
1049
- addressesByContractName: addressesByContractName,
1050
- contractNameByAddress: partition.contractNameByAddress
1018
+ addressesByContractName: addressesByContractName
1051
1019
  });
1052
1020
  return;
1053
1021
  }
@@ -1060,8 +1028,7 @@ function pushQueriesForRange(queries, partitionId, rangeFromBlock, rangeEndBlock
1060
1028
  chainId: 0,
1061
1029
  progress: 0,
1062
1030
  selection: selection,
1063
- addressesByContractName: addressesByContractName,
1064
- contractNameByAddress: partition.contractNameByAddress
1031
+ addressesByContractName: addressesByContractName
1065
1032
  });
1066
1033
  }
1067
1034
 
@@ -1140,8 +1107,7 @@ function getNextQuery(fetchState, budget, chainPendingBudget) {
1140
1107
  chainId: 0,
1141
1108
  progress: 0,
1142
1109
  selection: selection,
1143
- addressesByContractName: addressesByContractName,
1144
- contractNameByAddress: p.contractNameByAddress
1110
+ addressesByContractName: addressesByContractName
1145
1111
  });
1146
1112
  chunkFromBlock = chunkToBlock + 1 | 0;
1147
1113
  chunkIdx = chunkIdx + 1 | 0;
@@ -1156,8 +1122,7 @@ function getNextQuery(fetchState, budget, chainPendingBudget) {
1156
1122
  chainId: 0,
1157
1123
  progress: 0,
1158
1124
  selection: selection,
1159
- addressesByContractName: addressesByContractName,
1160
- contractNameByAddress: p.contractNameByAddress
1125
+ addressesByContractName: addressesByContractName
1161
1126
  });
1162
1127
  }
1163
1128
  } else {
@@ -1170,8 +1135,7 @@ function getNextQuery(fetchState, budget, chainPendingBudget) {
1170
1135
  chainId: 0,
1171
1136
  progress: 0,
1172
1137
  selection: selection,
1173
- addressesByContractName: addressesByContractName,
1174
- contractNameByAddress: p.contractNameByAddress
1138
+ addressesByContractName: addressesByContractName
1175
1139
  });
1176
1140
  }
1177
1141
  }
@@ -1219,8 +1183,7 @@ function getNextQuery(fetchState, budget, chainPendingBudget) {
1219
1183
  chainId: 0,
1220
1184
  progress: 0,
1221
1185
  selection: selection$1,
1222
- addressesByContractName: addressesByContractName$1,
1223
- contractNameByAddress: p.contractNameByAddress
1186
+ addressesByContractName: addressesByContractName$1
1224
1187
  });
1225
1188
  chunkFromBlock$1 = chunkToBlock$1 + 1 | 0;
1226
1189
  chunkIdx$1 = chunkIdx$1 + 1 | 0;
@@ -1235,8 +1198,7 @@ function getNextQuery(fetchState, budget, chainPendingBudget) {
1235
1198
  chainId: 0,
1236
1199
  progress: 0,
1237
1200
  selection: selection$1,
1238
- addressesByContractName: addressesByContractName$1,
1239
- contractNameByAddress: p.contractNameByAddress
1201
+ addressesByContractName: addressesByContractName$1
1240
1202
  });
1241
1203
  }
1242
1204
  } else {
@@ -1249,8 +1211,7 @@ function getNextQuery(fetchState, budget, chainPendingBudget) {
1249
1211
  chainId: 0,
1250
1212
  progress: 0,
1251
1213
  selection: selection$1,
1252
- addressesByContractName: addressesByContractName$1,
1253
- contractNameByAddress: p.contractNameByAddress
1214
+ addressesByContractName: addressesByContractName$1
1254
1215
  });
1255
1216
  }
1256
1217
  }
@@ -1352,7 +1313,6 @@ function make$1(startBlock, endBlock, eventConfigs, addresses, maxAddrInPartitio
1352
1313
  dependsOnAddresses: false
1353
1314
  },
1354
1315
  addressesByContractName: {},
1355
- contractNameByAddress: {},
1356
1316
  mergeBlock: undefined,
1357
1317
  dynamicContract: undefined,
1358
1318
  mutPendingQueries: [],
@@ -1529,7 +1489,6 @@ function rollback(fetchState, targetBlockNumber) {
1529
1489
  latestFetchedBlock: p.latestFetchedBlock,
1530
1490
  selection: p.selection,
1531
1491
  addressesByContractName: rollbackedAddressesByContractName,
1532
- contractNameByAddress: p.contractNameByAddress,
1533
1492
  mergeBlock: mergeBlock$1,
1534
1493
  dynamicContract: p.dynamicContract,
1535
1494
  mutPendingQueries: rollbackPendingQueries(p.mutPendingQueries, targetBlockNumber),
@@ -1551,7 +1510,6 @@ function rollback(fetchState, targetBlockNumber) {
1551
1510
  }) : p.latestFetchedBlock,
1552
1511
  selection: p.selection,
1553
1512
  addressesByContractName: p.addressesByContractName,
1554
- contractNameByAddress: p.contractNameByAddress,
1555
1513
  mergeBlock: p.mergeBlock,
1556
1514
  dynamicContract: p.dynamicContract,
1557
1515
  mutPendingQueries: rollbackPendingQueries(p.mutPendingQueries, targetBlockNumber),
@@ -1597,7 +1555,6 @@ function resetPendingQueries(fetchState) {
1597
1555
  latestFetchedBlock: partition.latestFetchedBlock,
1598
1556
  selection: partition.selection,
1599
1557
  addressesByContractName: partition.addressesByContractName,
1600
- contractNameByAddress: partition.contractNameByAddress,
1601
1558
  mergeBlock: partition.mergeBlock,
1602
1559
  dynamicContract: partition.dynamicContract,
1603
1560
  mutPendingQueries: kept,
@@ -1804,4 +1761,4 @@ export {
1804
1761
  getProgressBlockNumberAt,
1805
1762
  updateKnownHeight,
1806
1763
  }
1807
- /* Utils Not a pure module */
1764
+ /* deriveContractNameByAddress Not a pure module */
package/src/Internal.res CHANGED
@@ -420,6 +420,11 @@ type eventConfig = private {
420
420
  // string set so the shared mask logic is ecosystem-agnostic; sources recover
421
421
  // the typed view where they need it.
422
422
  selectedTransactionFields: Utils.Set.t<string>,
423
+ // `selectedTransactionFields` precompiled to the transaction-store selection
424
+ // bitmask (bit per ecosystem field code). Materialisation reads this per item
425
+ // so each transaction decodes only the fields its event selected. `0.` when
426
+ // nothing is selected or the ecosystem carries the transaction inline (Fuel).
427
+ transactionFieldMask: float,
423
428
  }
424
429
 
425
430
  type fuelEventKind =
package/src/Main.res CHANGED
@@ -687,6 +687,10 @@ let dropSchema = async () => {
687
687
  await persistence.storage.close()
688
688
  }
689
689
 
690
+ // Rejection carried by `onError`: the failure is already logged with full
691
+ // context, so callers should act on it (exit / re-throw) without logging again.
692
+ exception FatalError(exn)
693
+
690
694
  let start = async (
691
695
  ~persistence: option<Persistence.t>=?,
692
696
  ~reset=false,
@@ -742,17 +746,20 @@ let start = async (
742
746
  | None => config
743
747
  }
744
748
  // The single fatal-error handler, invoked once via IndexerState.errorExit.
749
+ // It logs the failure once (with chain context) and rejects the run wrapped in
750
+ // `FatalError` so callers know it's already logged — `Bin.res` just exits, the
751
+ // test worker unwraps and re-throws it to the parent thread. `runUntilFatalError`
752
+ // only ever rejects: on a clean run it stays pending and the process exits via
753
+ // ExitOnCaughtUp / when the indexer loop drains.
754
+ let onErrorReject = ref(None)
755
+ let runUntilFatalError: promise<unit> = Promise.make((_resolve, reject) =>
756
+ onErrorReject := Some(reject)
757
+ )
758
+ // `onErrorReject` is filled synchronously by `Promise.make` above, before the
759
+ // indexer can run and call `onError`, so it's always present here.
745
760
  let onError = (errHandler: ErrorHandling.t) => {
746
761
  errHandler->ErrorHandling.log
747
- if isTest {
748
- // The TestIndexer runs the indexer in a worker thread and reads the
749
- // failure off the worker 'error' event. Re-throw the original error
750
- // outside the promise chain on the next tick so the test sees its real
751
- // message instead of a generic non-zero exit code.
752
- NodeJs.setImmediate(() => errHandler->ErrorHandling.raiseExn)
753
- } else {
754
- NodeJs.process->NodeJs.exitWithCode(Failure)
755
- }
762
+ (onErrorReject.contents->Option.getUnsafe)(FatalError(errHandler.exn->Utils.prettifyExn))
756
763
  }
757
764
  let envioVersion = Utils.EnvioPackage.value.version
758
765
  Prometheus.Info.set(~version=envioVersion)
@@ -793,4 +800,5 @@ let start = async (
793
800
  }
794
801
  indexerStateRef := Some(state)
795
802
  state->IndexerLoop.start
803
+ await runUntilFatalError
796
804
  }
package/src/Main.res.mjs CHANGED
@@ -522,6 +522,8 @@ async function dropSchema() {
522
522
  return await persistence.storage.close();
523
523
  }
524
524
 
525
+ let FatalError = /* @__PURE__ */Primitive_exceptions.create("Main.FatalError");
526
+
525
527
  async function start(persistence, resetOpt, isTestOpt, exitAfterFirstEventBlockOpt, patchConfig) {
526
528
  let reset = resetOpt !== undefined ? resetOpt : false;
527
529
  let isTest = isTestOpt !== undefined ? isTestOpt : false;
@@ -562,13 +564,18 @@ async function start(persistence, resetOpt, isTestOpt, exitAfterFirstEventBlockO
562
564
  allEnums: config.allEnums
563
565
  }) : config;
564
566
  let config$2 = patchConfig !== undefined ? patchConfig(config$1, registrations) : config$1;
567
+ let onErrorReject = {
568
+ contents: undefined
569
+ };
570
+ let runUntilFatalError = new Promise((_resolve, reject) => {
571
+ onErrorReject.contents = reject;
572
+ });
565
573
  let onError = errHandler => {
566
574
  ErrorHandling.log(errHandler);
567
- if (isTest) {
568
- setImmediate(() => ErrorHandling.raiseExn(errHandler));
569
- } else {
570
- Process.exit(1);
571
- }
575
+ onErrorReject.contents({
576
+ RE_EXN_ID: FatalError,
577
+ _1: Utils.prettifyExn(errHandler.exn)
578
+ });
572
579
  };
573
580
  let envioVersion = Utils.EnvioPackage.value.version;
574
581
  Prometheus.Info.set(envioVersion);
@@ -599,7 +606,8 @@ async function start(persistence, resetOpt, isTestOpt, exitAfterFirstEventBlockO
599
606
  Tui.start(() => state);
600
607
  }
601
608
  indexerStateRef.contents = Primitive_option.some(state);
602
- return IndexerLoop.start(state);
609
+ IndexerLoop.start(state);
610
+ return await runUntilFatalError;
603
611
  }
604
612
 
605
613
  export {
@@ -616,6 +624,7 @@ export {
616
624
  getEnvioInfo,
617
625
  migrate,
618
626
  dropSchema,
627
+ FatalError,
619
628
  start,
620
629
  }
621
630
  /* chainDataSchema Not a pure module */
@@ -234,6 +234,10 @@ let deriveSrcAddress = (
234
234
  // A non-wildcard event is gated by `clientAddressFilter` on srcAddress ownership,
235
235
  // so a simulate item whose srcAddress isn't indexed on this chain would be
236
236
  // silently dropped (handler never runs). Fail loudly instead.
237
+ //
238
+ // `config` must already have handler registrations applied, otherwise an event
239
+ // made wildcard purely through `indexer.onEvent({ wildcard: true })` reads back
240
+ // as non-wildcard and gets wrongly rejected.
237
241
  let validateSrcAddresses = (
238
242
  ~simulateItems: array<JSON.t>,
239
243
  ~config: Config.t,
@@ -667,8 +667,11 @@ let makeCreateTestIndexer = (~config: Config.t, ~workerPath: string): (
667
667
  Int.compare(aId, bId)
668
668
  })
669
669
 
670
- // Parse and validate all chain configs upfront before starting any workers
671
- let preflightAddressesByChain = getIndexingAddressesByChain(state)
670
+ // Parse and validate the block ranges upfront before starting any
671
+ // workers. srcAddress validation can't run here: it depends on which
672
+ // events are wildcard, and `config` reflects config.yaml only —
673
+ // handler-level `wildcard: true` takes effect only once registrations
674
+ // are applied.
672
675
  let chainEntries = sortedChainKeys->Array.map(chainIdStr => {
673
676
  let rawChainConfig = rawChains->Dict.getUnsafe(chainIdStr)
674
677
  let chainId = switch chainIdStr->Int.fromString {
@@ -684,19 +687,6 @@ let makeCreateTestIndexer = (~config: Config.t, ~workerPath: string): (
684
687
  ~rawChainConfig,
685
688
  ~progressBlock=state.progressBlockByChain->Dict.get(chainIdStr),
686
689
  )
687
- switch rawChainConfig.simulate {
688
- | Some(simulateItems) =>
689
- let chainConfig = config.chainMap->ChainMap.get(ChainMap.Chain.makeUnsafe(~chainId))
690
- SimulateItems.validateSrcAddresses(
691
- ~simulateItems,
692
- ~config,
693
- ~chainConfig,
694
- ~indexingAddresses=preflightAddressesByChain
695
- ->Dict.get(chainIdStr)
696
- ->Option.getOr([]),
697
- )
698
- | None => ()
699
- }
700
690
  (chainIdStr, chainId, rawChainConfig, processChainConfig)
701
691
  })
702
692
 
@@ -877,7 +867,27 @@ let initTestWorker = () => {
877
867
  | Some(_) => ()
878
868
  }
879
869
 
880
- let patchConfig = (config, _registrations) => {
870
+ let patchConfig = (config: Config.t, _registrations) => {
871
+ // `config` here has handler registrations applied, so `isWildcard`
872
+ // reflects `indexer.onEvent({ wildcard: true })` — which the main thread
873
+ // can't see. Validate srcAddresses against it before patching in the
874
+ // simulate source.
875
+ switch simulate {
876
+ | Some(simulateItems) =>
877
+ let chainConfig = config.chainMap->ChainMap.get(ChainMap.Chain.makeUnsafe(~chainId))
878
+ let indexingAddresses =
879
+ initialState.chains
880
+ ->Array.find(c => c.id == chainId)
881
+ ->Option.mapOr([], c => c.indexingAddresses)
882
+ SimulateItems.validateSrcAddresses(
883
+ ~simulateItems,
884
+ ~config,
885
+ ~chainConfig,
886
+ ~indexingAddresses,
887
+ )
888
+ | None => ()
889
+ }
890
+
881
891
  let config = SimulateItems.patchConfig(~config, ~processConfig)
882
892
 
883
893
  // In auto-exit mode, set batchSize=1 to process one block checkpoint at a time
@@ -887,7 +897,23 @@ let initTestWorker = () => {
887
897
  config
888
898
  }
889
899
  }
890
- Main.start(~persistence, ~isTest=true, ~patchConfig, ~exitAfterFirstEventBlock)->ignore
900
+ Main.start(~persistence, ~isTest=true, ~patchConfig, ~exitAfterFirstEventBlock)
901
+ ->Promise.catch(exn => {
902
+ // `Main.start` rejects on any fatal error: a runtime failure arrives wrapped
903
+ // in `Main.FatalError` (already logged), a setup throw (e.g. patchConfig's
904
+ // srcAddress validation) arrives raw. The parent only learns of failures
905
+ // through the worker `error` event, which fires on an *uncaught* exception.
906
+ // Throwing synchronously in this catch would just reject the catch's own
907
+ // promise (swallowed by `ignore`); `setImmediate` re-throws outside the
908
+ // promise chain so it becomes uncaught and reaches the parent.
909
+ let toThrow = switch exn {
910
+ | Main.FatalError(inner) => inner
911
+ | _ => exn->Utils.prettifyExn
912
+ }
913
+ NodeJs.setImmediate(() => throw(toThrow))
914
+ Promise.resolve()
915
+ })
916
+ ->ignore
891
917
  | None =>
892
918
  Logging.error("TestIndexerWorker: No worker data provided")
893
919
  NodeJs.process->NodeJs.exitWithCode(Failure)
@@ -481,17 +481,11 @@ function makeCreateTestIndexer(config, workerPath) {
481
481
  let bId = Stdlib_Option.getOr(Stdlib_Int.fromString(b, undefined), 0);
482
482
  return Primitive_int.compare(aId, bId);
483
483
  });
484
- let preflightAddressesByChain = getIndexingAddressesByChain(state);
485
484
  let chainEntries = sortedChainKeys.map(chainIdStr => {
486
485
  let rawChainConfig = rawChains[chainIdStr];
487
486
  let id = Stdlib_Int.fromString(chainIdStr, undefined);
488
487
  let chainId = id !== undefined ? id : Stdlib_JsError.throwWithMessage(`Invalid chain ID "` + chainIdStr + `": expected a numeric chain ID`);
489
488
  let processChainConfig = parseBlockRange(chainIdStr, config, rawChainConfig, state.progressBlockByChain[chainIdStr]);
490
- let simulateItems = rawChainConfig.simulate;
491
- if (simulateItems !== undefined) {
492
- let chainConfig = ChainMap.get(config.chainMap, ChainMap.Chain.makeUnsafe(chainId));
493
- SimulateItems.validateSrcAddresses(simulateItems, config, chainConfig, Stdlib_Option.getOr(preflightAddressesByChain[chainIdStr], []));
494
- }
495
489
  return [
496
490
  chainIdStr,
497
491
  chainId,
@@ -592,9 +586,11 @@ function initTestWorker() {
592
586
  Process.exit(1);
593
587
  return;
594
588
  }
589
+ let initialState = workerData.initialState;
595
590
  let simulate = workerData.simulate;
596
591
  let endBlock = workerData.endBlock;
597
- let chainIdStr = workerData.chainId.toString();
592
+ let chainId = workerData.chainId;
593
+ let chainIdStr = chainId.toString();
598
594
  let exitAfterFirstEventBlock = Stdlib_Option.isNone(endBlock);
599
595
  let resolvedChainDict = {};
600
596
  resolvedChainDict["startBlock"] = workerData.startBlock;
@@ -609,7 +605,7 @@ function initTestWorker() {
609
605
  let processConfig = {
610
606
  chains: resolvedChainsDict
611
607
  };
612
- let proxy = TestIndexerProxyStorage.make(parentPort, workerData.initialState);
608
+ let proxy = TestIndexerProxyStorage.make(parentPort, initialState);
613
609
  let storage = TestIndexerProxyStorage.makeStorage(proxy);
614
610
  let config = Config.loadWithoutRegistrations();
615
611
  let persistence = Persistence.make(config.userEntities, config.allEnums, storage);
@@ -619,6 +615,11 @@ function initTestWorker() {
619
615
  Logging.setLogLevel("silent");
620
616
  }
621
617
  let patchConfig = (config, _registrations) => {
618
+ if (simulate !== undefined) {
619
+ let chainConfig = ChainMap.get(config.chainMap, ChainMap.Chain.makeUnsafe(chainId));
620
+ let indexingAddresses = Stdlib_Option.mapOr(initialState.chains.find(c => c.id === chainId), [], c => c.indexingAddresses);
621
+ SimulateItems.validateSrcAddresses(simulate, config, chainConfig, indexingAddresses);
622
+ }
622
623
  let config$1 = SimulateItems.patchConfig(config, processConfig);
623
624
  if (exitAfterFirstEventBlock) {
624
625
  return {
@@ -646,7 +647,13 @@ function initTestWorker() {
646
647
  return config$1;
647
648
  }
648
649
  };
649
- Main.start(persistence, undefined, true, exitAfterFirstEventBlock, patchConfig);
650
+ Stdlib_Promise.$$catch(Main.start(persistence, undefined, true, exitAfterFirstEventBlock, patchConfig), exn => {
651
+ let toThrow = exn.RE_EXN_ID === Main.FatalError ? exn._1 : Utils.prettifyExn(exn);
652
+ setImmediate(() => {
653
+ throw toThrow;
654
+ });
655
+ return Promise.resolve();
656
+ });
650
657
  }
651
658
 
652
659
  export {
@@ -52,7 +52,9 @@ let transactionFields = [
52
52
  "authorizationList",
53
53
  ]
54
54
 
55
- let transactionFieldMask = TransactionStore.makeMaskFn(transactionFields)
55
+ // One event's selected transaction fields → store selection bitmask. Computed
56
+ // per event at config build and cached on the event config.
57
+ let eventTransactionFieldMask = TransactionStore.makeMaskFn(transactionFields)
56
58
 
57
59
  let cleanUpRawEventFieldsInPlace: JSON.t => unit = %raw(`fields => {
58
60
  delete fields.hash
@@ -83,7 +85,6 @@ let make = (~logger: Pino.t): Ecosystem.t => {
83
85
  s.field("block", S.option(S.object(s2 => s2.field("number", S.unknown))))
84
86
  ),
85
87
  logger,
86
- transactionFieldMask,
87
88
  // The payload carries `transaction` by batch prep (HyperSync) or inline
88
89
  // (RPC/simulate), so the event is the payload as-is.
89
90
  toEvent: eventItem => eventItem.payload->(Utils.magic: Internal.eventPayload => Internal.event),
@@ -101,10 +102,16 @@ let make = (~logger: Pino.t): Ecosystem.t => {
101
102
  ),
102
103
  toRawEvent: eventItem => {
103
104
  let payload = eventItem.payload->toPayload
105
+ let eventConfig =
106
+ eventItem.eventConfig->(Utils.magic: Internal.eventConfig => Internal.evmEventConfig)
104
107
  eventItem->RawEvent.make(
105
108
  ~block=payload.block,
106
109
  ~transaction=payload.transaction,
107
- ~params=payload.params,
110
+ // The decoder emits `{}` for zero-parameter events, which the params
111
+ // schema rejects; pass unit so it serializes to the "null" sentinel.
112
+ ~params=eventConfig.paramsMetadata->Array.length == 0
113
+ ? ()->(Utils.magic: unit => Internal.eventParams)
114
+ : payload.params,
108
115
  ~srcAddress=payload.srcAddress,
109
116
  ~cleanUpRawEventFieldsInPlace,
110
117
  )
@@ -40,7 +40,7 @@ let transactionFields = [
40
40
  "authorizationList"
41
41
  ];
42
42
 
43
- let transactionFieldMask = TransactionStore.makeMaskFn(transactionFields);
43
+ let eventTransactionFieldMask = TransactionStore.makeMaskFn(transactionFields);
44
44
 
45
45
  let cleanUpRawEventFieldsInPlace = (fields => {
46
46
  delete fields.hash
@@ -60,7 +60,6 @@ function make(logger) {
60
60
  onEventBlockFilterSchema: S$RescriptSchema.object(s => s.f("block", S$RescriptSchema.option(S$RescriptSchema.object(s2 => s2.f("number", S$RescriptSchema.unknown))))),
61
61
  logger: logger,
62
62
  toEvent: eventItem => eventItem.payload,
63
- transactionFieldMask: transactionFieldMask,
64
63
  toEventLogger: eventItem => Logging.createChildFrom(logger, {
65
64
  contract: eventItem.eventConfig.contractName,
66
65
  event: eventItem.eventConfig.name,
@@ -71,15 +70,16 @@ function make(logger) {
71
70
  }),
72
71
  toRawEvent: eventItem => {
73
72
  let payload = eventItem.payload;
74
- return RawEvent.make(eventItem, payload.block, payload.transaction, payload.params, payload.srcAddress, cleanUpRawEventFieldsInPlace);
73
+ let eventConfig = eventItem.eventConfig;
74
+ return RawEvent.make(eventItem, payload.block, payload.transaction, eventConfig.paramsMetadata.length === 0 ? undefined : payload.params, payload.srcAddress, cleanUpRawEventFieldsInPlace);
75
75
  }
76
76
  };
77
77
  }
78
78
 
79
79
  export {
80
80
  transactionFields,
81
- transactionFieldMask,
81
+ eventTransactionFieldMask,
82
82
  cleanUpRawEventFieldsInPlace,
83
83
  make,
84
84
  }
85
- /* transactionFieldMask Not a pure module */
85
+ /* eventTransactionFieldMask Not a pure module */
@@ -39,8 +39,6 @@ let make = (~logger: Pino.t): Ecosystem.t => {
39
39
  s.field("block", S.option(S.object(s2 => s2.field("height", S.unknown))))
40
40
  ),
41
41
  logger,
42
- // Fuel carries the transaction inline on the payload.
43
- transactionFieldMask: _ => 0.,
44
42
  toEvent: eventItem => eventItem.payload->(Utils.magic: Internal.eventPayload => Internal.event),
45
43
  toEventLogger: eventItem =>
46
44
  Logging.createChildFrom(
@@ -22,7 +22,6 @@ function make(logger) {
22
22
  onEventBlockFilterSchema: S$RescriptSchema.object(s => s.f("block", S$RescriptSchema.option(S$RescriptSchema.object(s2 => s2.f("height", S$RescriptSchema.unknown))))),
23
23
  logger: logger,
24
24
  toEvent: eventItem => eventItem.payload,
25
- transactionFieldMask: param => 0,
26
25
  toEventLogger: eventItem => Logging.createChildFrom(logger, {
27
26
  contract: eventItem.eventConfig.contractName,
28
27
  event: eventItem.eventConfig.name,
@@ -676,7 +676,7 @@ let executeQuery = async (
676
676
  ~fromBlock=query.fromBlock,
677
677
  ~toBlock,
678
678
  ~addressesByContractName=query.addressesByContractName,
679
- ~contractNameByAddress=query.contractNameByAddress,
679
+ ~contractNameByAddress=query.addressesByContractName->FetchState.deriveContractNameByAddress,
680
680
  ~partitionId=query.partitionId,
681
681
  ~knownHeight,
682
682
  ~selection=query.selection,