envio 3.3.0-alpha.2 → 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.
Files changed (71) hide show
  1. package/index.d.ts +23 -21
  2. package/package.json +6 -6
  3. package/src/Bin.res +3 -0
  4. package/src/Bin.res.mjs +4 -0
  5. package/src/ChainFetching.res +26 -3
  6. package/src/ChainFetching.res.mjs +22 -3
  7. package/src/ChainState.res +3 -12
  8. package/src/ChainState.res.mjs +6 -8
  9. package/src/ChainState.resi +0 -2
  10. package/src/CrossChainState.res +15 -0
  11. package/src/CrossChainState.res.mjs +12 -0
  12. package/src/Ecosystem.res +0 -6
  13. package/src/EventConfigBuilder.res +45 -18
  14. package/src/EventConfigBuilder.res.mjs +28 -10
  15. package/src/EventProcessing.res +53 -37
  16. package/src/EventProcessing.res.mjs +41 -36
  17. package/src/FetchState.res +35 -42
  18. package/src/FetchState.res.mjs +37 -63
  19. package/src/HandlerLoader.res +10 -1
  20. package/src/HandlerLoader.res.mjs +15 -8
  21. package/src/Internal.res +8 -3
  22. package/src/Internal.res.mjs +1 -1
  23. package/src/LoadLayer.res +5 -5
  24. package/src/LoadLayer.res.mjs +6 -6
  25. package/src/Main.res +17 -9
  26. package/src/Main.res.mjs +15 -6
  27. package/src/PgStorage.res +4 -4
  28. package/src/PgStorage.res.mjs +5 -5
  29. package/src/Prometheus.res +10 -10
  30. package/src/Prometheus.res.mjs +9 -9
  31. package/src/PruneStaleHistory.res +2 -2
  32. package/src/PruneStaleHistory.res.mjs +3 -3
  33. package/src/Rollback.res +2 -2
  34. package/src/Rollback.res.mjs +3 -3
  35. package/src/SimulateItems.res +81 -15
  36. package/src/SimulateItems.res.mjs +63 -20
  37. package/src/TestIndexer.res +77 -39
  38. package/src/TestIndexer.res.mjs +45 -31
  39. package/src/bindings/Performance.res +7 -0
  40. package/src/bindings/Performance.res.mjs +21 -0
  41. package/src/bindings/Performance.resi +7 -0
  42. package/src/sources/EventRouter.res +17 -21
  43. package/src/sources/EventRouter.res.mjs +6 -9
  44. package/src/sources/Evm.res +10 -37
  45. package/src/sources/Evm.res.mjs +5 -40
  46. package/src/sources/Fuel.res +0 -4
  47. package/src/sources/Fuel.res.mjs +0 -7
  48. package/src/sources/HyperFuelSource.res +10 -11
  49. package/src/sources/HyperFuelSource.res.mjs +11 -11
  50. package/src/sources/HyperSync.res +3 -0
  51. package/src/sources/HyperSync.res.mjs +1 -0
  52. package/src/sources/HyperSync.resi +1 -0
  53. package/src/sources/HyperSyncClient.res +5 -1
  54. package/src/sources/HyperSyncSource.res +42 -24
  55. package/src/sources/HyperSyncSource.res.mjs +30 -26
  56. package/src/sources/RpcSource.res +7 -8
  57. package/src/sources/RpcSource.res.mjs +8 -8
  58. package/src/sources/SimulateSource.res +1 -1
  59. package/src/sources/Source.res +1 -1
  60. package/src/sources/SourceManager.res +17 -16
  61. package/src/sources/SourceManager.res.mjs +10 -15
  62. package/src/sources/Svm.res +6 -11
  63. package/src/sources/Svm.res.mjs +6 -14
  64. package/src/sources/SvmHyperSyncSource.res +24 -18
  65. package/src/sources/SvmHyperSyncSource.res.mjs +22 -15
  66. package/src/sources/TransactionStore.res +62 -35
  67. package/src/sources/TransactionStore.res.mjs +44 -18
  68. package/svm.schema.json +30 -37
  69. package/src/bindings/Hrtime.res +0 -58
  70. package/src/bindings/Hrtime.res.mjs +0 -90
  71. package/src/bindings/Hrtime.resi +0 -30
package/index.d.ts CHANGED
@@ -997,24 +997,6 @@ export type SvmTokenBalance = {
997
997
  readonly postAmount?: string;
998
998
  };
999
999
 
1000
- /** Parent transaction surfaced when an instruction's
1001
- * `include_transaction` flag is `true`. */
1002
- export type SvmTransaction = {
1003
- readonly signatures: readonly string[];
1004
- readonly feePayer?: string;
1005
- readonly success?: boolean;
1006
- readonly err?: string;
1007
- /** Lamports. */
1008
- readonly fee?: bigint;
1009
- readonly computeUnitsConsumed?: bigint;
1010
- readonly accountKeys: readonly string[];
1011
- readonly recentBlockhash?: string;
1012
- readonly version?: string;
1013
- /** SPL Token / Token-2022 balance snapshots for this transaction.
1014
- * Present when `include_token_balances` is `true`. */
1015
- readonly tokenBalances?: readonly SvmTokenBalance[];
1016
- };
1017
-
1018
1000
  export type SvmLog = {
1019
1001
  readonly kind: string;
1020
1002
  readonly message: string;
@@ -1033,6 +1015,7 @@ export type SvmLog = {
1033
1015
  * are base58 strings. */
1034
1016
  export type SvmInstruction<
1035
1017
  Params extends SvmInstructionParams = SvmInstructionParams,
1018
+ Tx = SvmTransaction,
1036
1019
  > = {
1037
1020
  /** Program name as declared under `programs[].name` in `config.yaml`. */
1038
1021
  readonly programName: string;
@@ -1050,8 +1033,11 @@ export type SvmInstruction<
1050
1033
  readonly d8?: string;
1051
1034
  /** Borsh-decoded params. Present when a schema is configured and matched. */
1052
1035
  readonly params?: Params;
1053
- /** Present when the instruction's `include_transaction` is `true`. */
1054
- readonly transaction?: SvmTransaction;
1036
+ /** Parent transaction. Carries only the fields selected via this
1037
+ * instruction's `field_selection`; unselected fields are typed as
1038
+ * `FieldNotSelected<...>` so reading them is a compile error. Always present
1039
+ * (`{}` when no fields are selected). */
1040
+ readonly transaction: Tx;
1055
1041
  /** Present when the instruction's `include_logs` is `true`; only logs
1056
1042
  * scoped to this exact instruction (matching `instruction_address`). */
1057
1043
  readonly logs?: readonly SvmLog[];
@@ -1327,7 +1313,10 @@ type SvmEcosystem<Config extends IndexerConfigTypes = GlobalConfig> =
1327
1313
  handler: (
1328
1314
  args: SvmOnInstructionHandlerArgs<
1329
1315
  Config,
1330
- SvmInstruction<SvmParamsFromProgramTable<Programs[P][I]>>
1316
+ SvmInstruction<
1317
+ SvmParamsFromProgramTable<Programs[P][I]>,
1318
+ Programs[P][I]["transaction"]
1319
+ >
1331
1320
  >,
1332
1321
  ) => Promise<void>,
1333
1322
  ) => void;
@@ -1695,6 +1684,7 @@ type EvmContractsT = GlobalConfig extends { evm: { contracts: infer X extends
1695
1684
  type FuelChainsT = GlobalConfig extends { fuel: { chains: infer X extends Record<string, { id: number }> } } ? X : {};
1696
1685
  type FuelContractsT = GlobalConfig extends { fuel: { contracts: infer X extends Record<string, Record<string, any>> } } ? X : {};
1697
1686
  type SvmChainsT = GlobalConfig extends { svm: { chains: infer X extends Record<string, { id: number }> } } ? X : {};
1687
+ type SvmProgramsT = GlobalConfig extends { svm: { programs: infer X extends Record<string, Record<string, any>> } } ? X : {};
1698
1688
  type EntitiesT = GlobalConfig extends { entities: infer X extends Record<string, object> } ? X : {};
1699
1689
  type EnumsT = GlobalConfig extends { enums: infer X extends Record<string, any> } ? X : {};
1700
1690
 
@@ -1716,6 +1706,18 @@ export type FuelChainId = IsEmptyObject<FuelChainsT> extends true ? NotConfigure
1716
1706
  /** Union of all configured SVM chain IDs. */
1717
1707
  export type SvmChainId = IsEmptyObject<SvmChainsT> extends true ? NotConfigured<"SvmChainId", "Configure SVM chains"> : SvmChainsT [keyof SvmChainsT ]["id"];
1718
1708
 
1709
+ /** The SVM parent-transaction type generated from this project's
1710
+ * `field_selection`: the union of every instruction's `transaction` shape,
1711
+ * with unselected fields typed as `FieldNotSelected<...>`. Resolves to a
1712
+ * `NotConfigured` hint until `envio codegen` augments {@link Global}. */
1713
+ export type SvmTransaction = IsEmptyObject<SvmProgramsT> extends true
1714
+ ? NotConfigured<"SvmTransaction", "Configure SVM programs">
1715
+ : {
1716
+ [P in keyof SvmProgramsT]: {
1717
+ [I in keyof SvmProgramsT[P]]: SvmProgramsT[P][I]["transaction"];
1718
+ }[keyof SvmProgramsT[P]];
1719
+ }[keyof SvmProgramsT];
1720
+
1719
1721
  /** Lookup an EVM event type by contract and event name. Without generics,
1720
1722
  * resolves to the discriminated union of every EVM event in the project. */
1721
1723
  export type EvmEvent<
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "envio",
3
- "version": "3.3.0-alpha.2",
3
+ "version": "3.3.0-alpha.4",
4
4
  "type": "module",
5
5
  "description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
6
6
  "bin": "./bin.mjs",
@@ -69,10 +69,10 @@
69
69
  "tsx": "4.21.0"
70
70
  },
71
71
  "optionalDependencies": {
72
- "envio-linux-x64": "3.3.0-alpha.2",
73
- "envio-linux-x64-musl": "3.3.0-alpha.2",
74
- "envio-linux-arm64": "3.3.0-alpha.2",
75
- "envio-darwin-x64": "3.3.0-alpha.2",
76
- "envio-darwin-arm64": "3.3.0-alpha.2"
72
+ "envio-linux-x64": "3.3.0-alpha.4",
73
+ "envio-linux-x64-musl": "3.3.0-alpha.4",
74
+ "envio-linux-arm64": "3.3.0-alpha.4",
75
+ "envio-darwin-x64": "3.3.0-alpha.4",
76
+ "envio-darwin-arm64": "3.3.0-alpha.4"
77
77
  }
78
78
  }
package/src/Bin.res CHANGED
@@ -71,6 +71,9 @@ let run = async args => {
71
71
  }
72
72
  }
73
73
  } catch {
74
+ | Main.FatalError(_) =>
75
+ // Already logged with full context by Main.start's onError; just exit.
76
+ NodeJs.process->NodeJs.exitWithCode(Failure)
74
77
  | exn =>
75
78
  // Log just the exception's own message — wrapping it in "Failed at
76
79
  // initialization" and pino's err serializer buries the real cause under
package/src/Bin.res.mjs CHANGED
@@ -79,6 +79,10 @@ async function run(args) {
79
79
  }
80
80
  } catch (raw_exn) {
81
81
  let exn = Primitive_exceptions.internalToException(raw_exn);
82
+ if (exn.RE_EXN_ID === Main.FatalError) {
83
+ Process.exit(1);
84
+ return;
85
+ }
82
86
  let e = Primitive_exceptions.internalToException(exn);
83
87
  let message = e.RE_EXN_ID === "JsExn" ? Stdlib_Option.getOr(Stdlib_JsExn.message(e._1), "Failed at initialization") : "Failed at initialization";
84
88
  Logging.error(message);
@@ -11,13 +11,12 @@ type partitionQueryResponse = {
11
11
  let runContractRegistersOrThrow = async (
12
12
  ~itemsWithContractRegister: array<Internal.item>,
13
13
  ~config: Config.t,
14
- ~chainState: ChainState.t,
15
14
  ~page: option<TransactionStore.t>,
16
15
  ) => {
17
16
  // contractRegister handlers can read event.transaction, so materialise the
18
17
  // selected fields onto the payloads before running them. All items belong to
19
18
  // the chain being fetched, hence its single page store and mask.
20
- await chainState->ChainState.materializePageItems(~items=itemsWithContractRegister, ~page)
19
+ await ChainState.materializePageItems(~items=itemsWithContractRegister, ~page)
21
20
 
22
21
  let itemsWithDcs = []
23
22
 
@@ -233,7 +232,6 @@ let rec onQueryResponse = async (
233
232
  switch await runContractRegistersOrThrow(
234
233
  ~itemsWithContractRegister,
235
234
  ~config=state->IndexerState.config,
236
- ~chainState,
237
235
  ~page=transactionStore,
238
236
  ) {
239
237
  | exception exn => IndexerState.errorExit(state, exn->ErrorHandling.make)
@@ -336,6 +334,15 @@ let fetchChain = async (
336
334
  // there's nothing to fetch. During backfill any such chain is idle.
337
335
  let reducedPolling = !isRealtime
338
336
 
337
+ // Accumulated across all queries dispatched in this tick so their per-query
338
+ // results collapse into a single completion log instead of one per query.
339
+ let dispatchedCount = switch action {
340
+ | FetchState.Ready(queries) => queries->Array.length
341
+ | WaitingForNewBlock | NothingToQuery => 0
342
+ }
343
+ let fetchedByPartition = Dict.make()
344
+ let fetchedCount = ref(0)
345
+
339
346
  // Owns its error boundary: launch doesn't catch, so any failure here (the
340
347
  // query, response handling, or dispatch itself) must stop the indexer.
341
348
  try {
@@ -361,6 +368,22 @@ let fetchChain = async (
361
368
  ~knownHeight=chainState->ChainState.knownHeight,
362
369
  ~isRealtime,
363
370
  )
371
+ fetchedCount := fetchedCount.contents + 1
372
+ fetchedByPartition->Dict.set(
373
+ query.partitionId,
374
+ {
375
+ "fromBlock": response.fromBlockQueried,
376
+ "toBlock": response.latestFetchedBlockNumber,
377
+ "numEvents": response.parsedQueueItems->Array.length,
378
+ },
379
+ )
380
+ if dispatchedCount > 0 && fetchedCount.contents === dispatchedCount {
381
+ Logging.trace({
382
+ "msg": "Finished querying",
383
+ "chainId": chain->ChainMap.Chain.toChainId,
384
+ "partitions": fetchedByPartition,
385
+ })
386
+ }
364
387
  await onQueryResponse(
365
388
  state,
366
389
  {chain, response, query},
@@ -16,8 +16,8 @@ import * as Stdlib_Promise from "@rescript/runtime/lib/es6/Stdlib_Promise.js";
16
16
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
17
17
  import * as ContractRegisterContext from "./ContractRegisterContext.res.mjs";
18
18
 
19
- async function runContractRegistersOrThrow(itemsWithContractRegister, config, chainState, page) {
20
- await ChainState.materializePageItems(chainState, itemsWithContractRegister, page);
19
+ async function runContractRegistersOrThrow(itemsWithContractRegister, config, page) {
20
+ await ChainState.materializePageItems(itemsWithContractRegister, page);
21
21
  let itemsWithDcs = [];
22
22
  let onRegister = (item, contractAddress, contractName) => {
23
23
  let dc_registrationBlock = item.blockNumber;
@@ -145,7 +145,7 @@ async function onQueryResponse(state, param, stateId, scheduleFetch, schedulePro
145
145
  }
146
146
  let newItemsWithDcs;
147
147
  try {
148
- newItemsWithDcs = await runContractRegistersOrThrow(itemsWithContractRegister, IndexerState.config(state), chainState, transactionStore);
148
+ newItemsWithDcs = await runContractRegistersOrThrow(itemsWithContractRegister, IndexerState.config(state), transactionStore);
149
149
  } catch (raw_exn) {
150
150
  let exn = Primitive_exceptions.internalToException(raw_exn);
151
151
  return IndexerState.errorExit(state, ErrorHandling.make(exn, undefined, undefined));
@@ -176,10 +176,29 @@ async function fetchChain(state, chain, action, stateId, scheduleFetch, schedule
176
176
  let isRealtime = IndexerState.isRealtime(state);
177
177
  let sourceManager = ChainState.sourceManager(chainState);
178
178
  let reducedPolling = !isRealtime;
179
+ let dispatchedCount;
180
+ dispatchedCount = typeof action !== "object" ? 0 : action._0.length;
181
+ let fetchedByPartition = {};
182
+ let fetchedCount = {
183
+ contents: 0
184
+ };
179
185
  try {
180
186
  return await ChainState.dispatch(chainState, async query => {
181
187
  try {
182
188
  let response = await SourceManager.executeQuery(sourceManager, query, ChainState.knownHeight(chainState), isRealtime);
189
+ fetchedCount.contents = fetchedCount.contents + 1 | 0;
190
+ fetchedByPartition[query.partitionId] = {
191
+ fromBlock: response.fromBlockQueried,
192
+ toBlock: response.latestFetchedBlockNumber,
193
+ numEvents: response.parsedQueueItems.length
194
+ };
195
+ if (dispatchedCount > 0 && fetchedCount.contents === dispatchedCount) {
196
+ Logging.trace({
197
+ msg: "Finished querying",
198
+ chainId: chain,
199
+ partitions: fetchedByPartition
200
+ });
201
+ }
183
202
  return await onQueryResponse(state, {
184
203
  chain: chain,
185
204
  response: response,
@@ -21,8 +21,6 @@ type t = {
21
21
  // transactionIndex). Fetch responses merge their page in; entries are pruned
22
22
  // as the chain progresses and dropped above the target on rollback.
23
23
  transactionStore: TransactionStore.t,
24
- // Bitmask of the transaction fields the store materialises at batch prep.
25
- transactionFieldMask: float,
26
24
  }
27
25
 
28
26
  // Per-chain shape returned by the status API.
@@ -67,7 +65,6 @@ let make = (
67
65
  ~numEventsProcessed=0.,
68
66
  ~timestampCaughtUpToHeadOrEndblock=None,
69
67
  ~isProgressAtHead=false,
70
- ~transactionFieldMask=0.,
71
68
  ~logger: Pino.t,
72
69
  ): t => {
73
70
  logger,
@@ -82,7 +79,6 @@ let make = (
82
79
  reorgDetection,
83
80
  safeCheckpointTracking,
84
81
  transactionStore: TransactionStore.make(),
85
- transactionFieldMask,
86
82
  }
87
83
 
88
84
  let makeInternal = (
@@ -328,7 +324,6 @@ let makeInternal = (
328
324
  ~committedProgressBlockNumber=progressBlockNumber,
329
325
  ~timestampCaughtUpToHeadOrEndblock,
330
326
  ~numEventsProcessed,
331
- ~transactionFieldMask=config.ecosystem.transactionFieldMask(eventConfigs),
332
327
  ~logger,
333
328
  )
334
329
  }
@@ -503,18 +498,14 @@ let isAtHeadWithoutEndBlock = (cs: t) =>
503
498
  // Materialise the chain store's selected transaction fields onto a batch's
504
499
  // items at batch prep (the persistent-store path).
505
500
  let materializeBatchItems = (cs: t, ~items: array<Internal.item>) =>
506
- cs.transactionStore->TransactionStore.materializeItems(~items, ~mask=cs.transactionFieldMask)
501
+ cs.transactionStore->TransactionStore.materializeItems(~items)
507
502
 
508
503
  // Materialise a fetch-response page's transactions onto its items before
509
504
  // contract-register handlers read them. `None` pages (RPC/Fuel/Simulate keep the
510
505
  // transaction inline) are a no-op.
511
- let materializePageItems = (
512
- cs: t,
513
- ~items: array<Internal.item>,
514
- ~page: option<TransactionStore.t>,
515
- ) =>
506
+ let materializePageItems = (~items: array<Internal.item>, ~page: option<TransactionStore.t>) =>
516
507
  switch page {
517
- | Some(store) => store->TransactionStore.materializeItems(~items, ~mask=cs.transactionFieldMask)
508
+ | Some(store) => store->TransactionStore.materializeItems(~items)
518
509
  | None => Promise.resolve()
519
510
  }
520
511
 
@@ -38,12 +38,11 @@ function configAddresses(chainConfig) {
38
38
  return addresses;
39
39
  }
40
40
 
41
- function make(chainConfig, fetchState, sourceManager, reorgDetection, committedProgressBlockNumber, safeCheckpointTrackingOpt, numEventsProcessedOpt, timestampCaughtUpToHeadOrEndblockOpt, isProgressAtHeadOpt, transactionFieldMaskOpt, logger) {
41
+ function make(chainConfig, fetchState, sourceManager, reorgDetection, committedProgressBlockNumber, safeCheckpointTrackingOpt, numEventsProcessedOpt, timestampCaughtUpToHeadOrEndblockOpt, isProgressAtHeadOpt, logger) {
42
42
  let safeCheckpointTracking = safeCheckpointTrackingOpt !== undefined ? Primitive_option.valFromOption(safeCheckpointTrackingOpt) : undefined;
43
43
  let numEventsProcessed = numEventsProcessedOpt !== undefined ? numEventsProcessedOpt : 0;
44
44
  let timestampCaughtUpToHeadOrEndblock = timestampCaughtUpToHeadOrEndblockOpt !== undefined ? Primitive_option.valFromOption(timestampCaughtUpToHeadOrEndblockOpt) : undefined;
45
45
  let isProgressAtHead = isProgressAtHeadOpt !== undefined ? isProgressAtHeadOpt : false;
46
- let transactionFieldMask = transactionFieldMaskOpt !== undefined ? transactionFieldMaskOpt : 0;
47
46
  return {
48
47
  logger: logger,
49
48
  fetchState: fetchState,
@@ -56,8 +55,7 @@ function make(chainConfig, fetchState, sourceManager, reorgDetection, committedP
56
55
  pendingBudget: 0,
57
56
  reorgDetection: reorgDetection,
58
57
  safeCheckpointTracking: safeCheckpointTracking,
59
- transactionStore: TransactionStore.make(),
60
- transactionFieldMask: transactionFieldMask
58
+ transactionStore: TransactionStore.make()
61
59
  };
62
60
  }
63
61
 
@@ -176,7 +174,7 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
176
174
  sources$1 = sources._0;
177
175
  break;
178
176
  }
179
- return make(chainConfig, fetchState, SourceManager.make(sources$1, isRealtime, undefined, undefined, undefined, reducedPollingInterval, undefined, undefined), ReorgDetection.make(chainReorgCheckpoints, maxReorgDepth, config.shouldRollbackOnReorg), progressBlockNumber, Primitive_option.some(SafeCheckpointTracking.make(maxReorgDepth, config.shouldRollbackOnReorg, chainReorgCheckpoints)), numEventsProcessed, Primitive_option.some(timestampCaughtUpToHeadOrEndblock), undefined, config.ecosystem.transactionFieldMask(eventConfigs), logger);
177
+ return make(chainConfig, fetchState, SourceManager.make(sources$1, isRealtime, undefined, undefined, undefined, reducedPollingInterval, undefined, undefined), ReorgDetection.make(chainReorgCheckpoints, maxReorgDepth, config.shouldRollbackOnReorg), progressBlockNumber, Primitive_option.some(SafeCheckpointTracking.make(maxReorgDepth, config.shouldRollbackOnReorg, chainReorgCheckpoints)), numEventsProcessed, Primitive_option.some(timestampCaughtUpToHeadOrEndblock), undefined, logger);
180
178
  }
181
179
 
182
180
  function makeFromConfig(chainConfig, config, registrations, knownHeight) {
@@ -326,12 +324,12 @@ function isAtHeadWithoutEndBlock(cs) {
326
324
  }
327
325
 
328
326
  function materializeBatchItems(cs, items) {
329
- return TransactionStore.materializeItems(cs.transactionStore, items, cs.transactionFieldMask);
327
+ return TransactionStore.materializeItems(cs.transactionStore, items);
330
328
  }
331
329
 
332
- function materializePageItems(cs, items, page) {
330
+ function materializePageItems(items, page) {
333
331
  if (page !== undefined) {
334
- return TransactionStore.materializeItems(Primitive_option.valFromOption(page), items, cs.transactionFieldMask);
332
+ return TransactionStore.materializeItems(Primitive_option.valFromOption(page), items);
335
333
  } else {
336
334
  return Promise.resolve();
337
335
  }
@@ -15,7 +15,6 @@ let make: (
15
15
  ~numEventsProcessed: float=?,
16
16
  ~timestampCaughtUpToHeadOrEndblock: option<Date.t>=?,
17
17
  ~isProgressAtHead: bool=?,
18
- ~transactionFieldMask: float=?,
19
18
  ~logger: Pino.t,
20
19
  ) => t
21
20
 
@@ -112,7 +111,6 @@ let handleQueryResult: (
112
111
  ) => unit
113
112
  let materializeBatchItems: (t, ~items: array<Internal.item>) => promise<unit>
114
113
  let materializePageItems: (
115
- t,
116
114
  ~items: array<Internal.item>,
117
115
  ~page: option<TransactionStore.t>,
118
116
  ) => promise<unit>
@@ -254,6 +254,21 @@ let checkAndFetch = async (
254
254
  idx := idx.contents + 1
255
255
  }
256
256
  admittedByChain->Dict.forEachWithKey((queries, chainId) => {
257
+ let partitions = Dict.make()
258
+ queries->Array.forEach((query: FetchState.query) =>
259
+ partitions->Dict.set(
260
+ query.partitionId,
261
+ {
262
+ "fromBlock": query.fromBlock,
263
+ "targetBlock": query.toBlock,
264
+ },
265
+ )
266
+ )
267
+ Logging.trace({
268
+ "msg": "Started querying",
269
+ "chainId": chainId->Int.fromString->Option.getUnsafe,
270
+ "partitions": partitions,
271
+ })
257
272
  actionByChain->Dict.set(chainId, FetchState.Ready(queries))
258
273
  // Mark the admitted queries in flight and reserve their size against the
259
274
  // shared budget; released as each response lands in handleQueryResult.
@@ -170,6 +170,18 @@ async function checkAndFetch(crossChainState, dispatchChain) {
170
170
  idx = idx + 1 | 0;
171
171
  };
172
172
  Stdlib_Dict.forEachWithKey(admittedByChain, (queries, chainId) => {
173
+ let partitions = {};
174
+ queries.forEach(query => {
175
+ partitions[query.partitionId] = {
176
+ fromBlock: query.fromBlock,
177
+ targetBlock: query.toBlock
178
+ };
179
+ });
180
+ Logging.trace({
181
+ msg: "Started querying",
182
+ chainId: Stdlib_Int.fromString(chainId, undefined),
183
+ partitions: partitions
184
+ });
173
185
  actionByChain[chainId] = {
174
186
  TAG: "Ready",
175
187
  _0: queries
package/src/Ecosystem.res CHANGED
@@ -2,8 +2,6 @@ type name = | @as("evm") Evm | @as("fuel") Fuel | @as("svm") Svm
2
2
 
3
3
  type t = {
4
4
  name: name,
5
- blockFields: array<string>,
6
- transactionFields: array<string>,
7
5
  blockNumberName: string,
8
6
  blockTimestampName: string,
9
7
  blockHashName: string,
@@ -36,10 +34,6 @@ type t = {
36
34
  registration from an item's opaque payload. `event.transaction` is written
37
35
  onto the payload at batch prep (HyperSync) or inline (RPC/simulate). */
38
36
  toEvent: Internal.eventItem => Internal.event,
39
- /** Bitmask (as a float) of the transaction fields selected across the chain's
40
- events — the set the store materialises at batch prep. `0.` when the
41
- ecosystem carries transactions inline. */
42
- transactionFieldMask: array<Internal.eventConfig> => float,
43
37
  /** Build the per-item child logger for an event item, with
44
38
  ecosystem-specific log fields (EVM/Fuel: contract/event/address; SVM:
45
39
  program/instruction/programId). Closes over the injected logger. */
@@ -326,28 +326,50 @@ let compileAddressFilter: string => (
326
326
  return new Function("event", "blockNumber", "indexingAddresses", body);
327
327
  }`)
328
328
 
329
- // Body of the client-side address filter for a DNF of address-filtered param
330
- // names (OR of AND-groups): keep the event only if some group's params are all
331
- // registered at or before the log's block. The DNF is fixed here, so it's
332
- // unrolled into one boolean expression no per-event closure, loop, or array.
333
- // `None` when there's no address-param filter. Exposed for snapshotting.
334
- let buildAddressFilterBody = (groups: array<array<string>>): option<string> => {
335
- switch groups {
329
+ // Body of the client-side address filter. Two analogous registered-at-or-before
330
+ // checks, ANDed: (1) for non-wildcard events, the log's srcAddress must itself be
331
+ // registered (ownership is resolved structurally by partition, but the temporal
332
+ // `effectiveStartBlock` gate lives here now); (2) a DNF of address-filtered param
333
+ // names (OR of AND-groups) for events that filter an indexed address param. The
334
+ // DNF is fixed here, so it's unrolled into one boolean expression — no per-event
335
+ // closure, loop, or array. `None` only for wildcard events without a param
336
+ // filter. Exposed for snapshotting.
337
+ // `srcAddressExpr` is the JS expression for the event's owning address: EVM and
338
+ // Fuel events expose `event.srcAddress`; SVM instructions expose `event.programId`.
339
+ let buildAddressFilterBody = (
340
+ groups: array<array<string>>,
341
+ ~isWildcard: bool,
342
+ ~srcAddressExpr: string="event.srcAddress",
343
+ ): option<string> => {
344
+ let paramLeaf = name =>
345
+ `(ic = indexingAddresses[p[${JSON.stringify(
346
+ JSON.String(name),
347
+ )}]]) !== undefined && ic.effectiveStartBlock <= blockNumber`
348
+ let paramDnf = switch groups {
336
349
  | [] => None
337
350
  | _ =>
338
- let leaf = name =>
339
- `(ic = indexingAddresses[p[${JSON.stringify(
340
- JSON.String(name),
341
- )}]]) !== undefined && ic.effectiveStartBlock <= blockNumber`
342
- let groupExprs =
343
- groups->Array.map(group => "(" ++ group->Array.map(leaf)->Array.join(" && ") ++ ")")
344
- Some("var p = event.params, ic; return " ++ groupExprs->Array.join(" || ") ++ ";")
351
+ Some(
352
+ groups
353
+ ->Array.map(group => "(" ++ group->Array.map(paramLeaf)->Array.join(" && ") ++ ")")
354
+ ->Array.join(" || "),
355
+ )
356
+ }
357
+ let srcLeaf = `(ic = indexingAddresses[${srcAddressExpr}]) !== undefined && ic.effectiveStartBlock <= blockNumber`
358
+ switch (isWildcard, paramDnf) {
359
+ | (true, None) => None
360
+ | (true, Some(dnf)) => Some("var p = event.params, ic; return " ++ dnf ++ ";")
361
+ | (false, None) => Some("var ic; return " ++ srcLeaf ++ ";")
362
+ | (false, Some(dnf)) =>
363
+ Some("var p = event.params, ic; return " ++ srcLeaf ++ " && (" ++ dnf ++ ");")
345
364
  }
346
365
  }
347
366
 
348
- let buildAddressFilter = (groups: array<array<string>>): option<
349
- (Internal.eventPayload, int, dict<Internal.indexingContract>) => bool,
350
- > => buildAddressFilterBody(groups)->Option.map(compileAddressFilter)
367
+ let buildAddressFilter = (
368
+ groups: array<array<string>>,
369
+ ~isWildcard: bool,
370
+ ~srcAddressExpr: string="event.srcAddress",
371
+ ): option<(Internal.eventPayload, int, dict<Internal.indexingContract>) => bool> =>
372
+ buildAddressFilterBody(groups, ~isWildcard, ~srcAddressExpr)->Option.map(compileAddressFilter)
351
373
 
352
374
  // ============== Build complete EVM event config ==============
353
375
 
@@ -415,11 +437,12 @@ let buildEvmEventConfig = (
415
437
  simulateParamsSchema: buildSimulateParamsSchema(params),
416
438
  getEventFiltersOrThrow,
417
439
  filterByAddresses,
418
- clientAddressFilter: ?buildAddressFilter(addressFilterParamGroups),
440
+ clientAddressFilter: ?buildAddressFilter(addressFilterParamGroups, ~isWildcard),
419
441
  dependsOnAddresses: !isWildcard || filterByAddresses,
420
442
  startBlock: resolvedStartBlock,
421
443
  selectedBlockFields,
422
444
  selectedTransactionFields,
445
+ transactionFieldMask: Evm.eventTransactionFieldMask(selectedTransactionFields),
423
446
  sighash,
424
447
  topicCount,
425
448
  paramsMetadata: params,
@@ -471,12 +494,14 @@ let buildSvmInstructionEventConfig = (
471
494
  simulateParamsSchema: paramsSchema,
472
495
  filterByAddresses: false,
473
496
  dependsOnAddresses: !isWildcard,
497
+ clientAddressFilter: ?buildAddressFilter([], ~isWildcard, ~srcAddressExpr="event.programId"),
474
498
  startBlock,
475
499
  programId,
476
500
  discriminator,
477
501
  discriminatorByteLen,
478
502
  includeLogs,
479
503
  selectedTransactionFields,
504
+ transactionFieldMask: Svm.eventTransactionFieldMask(selectedTransactionFields),
480
505
  accountFilters,
481
506
  isInner,
482
507
  accounts,
@@ -541,9 +566,11 @@ let buildFuelEventConfig = (
541
566
  simulateParamsSchema: paramsSchema,
542
567
  filterByAddresses: false,
543
568
  dependsOnAddresses: !isWildcard,
569
+ clientAddressFilter: ?buildAddressFilter([], ~isWildcard),
544
570
  startBlock,
545
571
  // Fuel keeps the transaction inline on the payload; nothing to materialise.
546
572
  selectedTransactionFields: Utils.Set.make(),
573
+ transactionFieldMask: 0.,
547
574
  kind: fuelKind,
548
575
  }
549
576
  }
@@ -1,5 +1,7 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
+ import * as Evm from "./sources/Evm.res.mjs";
4
+ import * as Svm from "./sources/Svm.res.mjs";
3
5
  import * as Utils from "./Utils.res.mjs";
4
6
  import * as Address from "./Address.res.mjs";
5
7
  import * as FuelSDK from "./sources/FuelSDK.res.mjs";
@@ -255,17 +257,27 @@ let compileAddressFilter = (function (body) {
255
257
  return new Function("event", "blockNumber", "indexingAddresses", body);
256
258
  });
257
259
 
258
- function buildAddressFilterBody(groups) {
259
- if (groups.length === 0) {
260
- return;
260
+ function buildAddressFilterBody(groups, isWildcard, srcAddressExprOpt) {
261
+ let srcAddressExpr = srcAddressExprOpt !== undefined ? srcAddressExprOpt : "event.srcAddress";
262
+ let paramLeaf = name => `(ic = indexingAddresses[p[` + JSON.stringify(name) + `]]) !== undefined && ic.effectiveStartBlock <= blockNumber`;
263
+ let paramDnf = groups.length !== 0 ? groups.map(group => "(" + group.map(paramLeaf).join(" && ") + ")").join(" || ") : undefined;
264
+ let srcLeaf = `(ic = indexingAddresses[` + srcAddressExpr + `]) !== undefined && ic.effectiveStartBlock <= blockNumber`;
265
+ if (isWildcard) {
266
+ if (paramDnf !== undefined) {
267
+ return "var p = event.params, ic; return " + paramDnf + ";";
268
+ } else {
269
+ return;
270
+ }
271
+ } else if (paramDnf !== undefined) {
272
+ return "var p = event.params, ic; return " + srcLeaf + " && (" + paramDnf + ");";
273
+ } else {
274
+ return "var ic; return " + srcLeaf + ";";
261
275
  }
262
- let leaf = name => `(ic = indexingAddresses[p[` + JSON.stringify(name) + `]]) !== undefined && ic.effectiveStartBlock <= blockNumber`;
263
- let groupExprs = groups.map(group => "(" + group.map(leaf).join(" && ") + ")");
264
- return "var p = event.params, ic; return " + groupExprs.join(" || ") + ";";
265
276
  }
266
277
 
267
- function buildAddressFilter(groups) {
268
- return Stdlib_Option.map(buildAddressFilterBody(groups), compileAddressFilter);
278
+ function buildAddressFilter(groups, isWildcard, srcAddressExprOpt) {
279
+ let srcAddressExpr = srcAddressExprOpt !== undefined ? srcAddressExprOpt : "event.srcAddress";
280
+ return Stdlib_Option.map(buildAddressFilterBody(groups, isWildcard, srcAddressExpr), compileAddressFilter);
269
281
  }
270
282
 
271
283
  function buildEvmEventConfig(contractName, eventName, sighash, params, isWildcard, handler, contractRegister, eventFilters, probeChainId, onEventBlockFilterSchema, blockFields, transactionFields, startBlock, globalBlockFieldsSetOpt, globalTransactionFieldsSetOpt) {
@@ -284,6 +296,7 @@ function buildEvmEventConfig(contractName, eventName, sighash, params, isWildcar
284
296
  let filterByAddresses = match.filterByAddresses;
285
297
  let resolvedStartBlock = whereStartBlock !== undefined ? whereStartBlock : startBlock;
286
298
  let match$1 = resolveFieldSelection(blockFields, transactionFields, globalBlockFieldsSet, globalTransactionFieldsSet);
299
+ let selectedTransactionFields = match$1[1];
287
300
  return {
288
301
  id: sighash + "_" + topicCount.toString(),
289
302
  name: eventName,
@@ -291,13 +304,14 @@ function buildEvmEventConfig(contractName, eventName, sighash, params, isWildcar
291
304
  isWildcard: isWildcard,
292
305
  filterByAddresses: filterByAddresses,
293
306
  dependsOnAddresses: !isWildcard || filterByAddresses,
294
- clientAddressFilter: Stdlib_Option.map(buildAddressFilterBody(match.addressFilterParamGroups), compileAddressFilter),
307
+ clientAddressFilter: buildAddressFilter(match.addressFilterParamGroups, isWildcard, undefined),
295
308
  handler: handler,
296
309
  contractRegister: contractRegister,
297
310
  paramsRawEventSchema: buildParamsSchema(params),
298
311
  simulateParamsSchema: buildSimulateParamsSchema(params),
299
312
  startBlock: resolvedStartBlock,
300
- selectedTransactionFields: match$1[1],
313
+ selectedTransactionFields: selectedTransactionFields,
314
+ transactionFieldMask: Evm.eventTransactionFieldMask(selectedTransactionFields),
301
315
  getEventFiltersOrThrow: match.getEventFiltersOrThrow,
302
316
  selectedBlockFields: match$1[0],
303
317
  sighash: sighash,
@@ -320,12 +334,14 @@ function buildSvmInstructionEventConfig(contractName, instructionName, programId
320
334
  isWildcard: isWildcard,
321
335
  filterByAddresses: false,
322
336
  dependsOnAddresses: !isWildcard,
337
+ clientAddressFilter: buildAddressFilter([], isWildcard, "event.programId"),
323
338
  handler: handler,
324
339
  contractRegister: contractRegister,
325
340
  paramsRawEventSchema: paramsSchema,
326
341
  simulateParamsSchema: paramsSchema,
327
342
  startBlock: startBlock,
328
343
  selectedTransactionFields: selectedTransactionFields,
344
+ transactionFieldMask: Svm.eventTransactionFieldMask(selectedTransactionFields),
329
345
  programId: programId,
330
346
  discriminator: discriminator,
331
347
  discriminatorByteLen: discriminatorByteLen,
@@ -388,12 +404,14 @@ function buildFuelEventConfig(contractName, eventName, kind, sighash, rawAbi, is
388
404
  isWildcard: isWildcard,
389
405
  filterByAddresses: false,
390
406
  dependsOnAddresses: !isWildcard,
407
+ clientAddressFilter: buildAddressFilter([], isWildcard, undefined),
391
408
  handler: handler,
392
409
  contractRegister: contractRegister,
393
410
  paramsRawEventSchema: paramsSchema,
394
411
  simulateParamsSchema: paramsSchema,
395
412
  startBlock: startBlock,
396
413
  selectedTransactionFields: new Set(),
414
+ transactionFieldMask: 0,
397
415
  kind: fuelKind
398
416
  };
399
417
  }