envio 3.3.0-alpha.1 → 3.3.0-alpha.2

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 (58) hide show
  1. package/package.json +6 -6
  2. package/src/Batch.res +0 -6
  3. package/src/Batch.res.mjs +0 -11
  4. package/src/BatchProcessing.res +2 -12
  5. package/src/BatchProcessing.res.mjs +2 -7
  6. package/src/ChainFetching.res +17 -6
  7. package/src/ChainFetching.res.mjs +11 -11
  8. package/src/ChainMetadata.res +1 -11
  9. package/src/ChainMetadata.res.mjs +1 -10
  10. package/src/ChainState.res +150 -1
  11. package/src/ChainState.res.mjs +138 -9
  12. package/src/ChainState.resi +51 -1
  13. package/src/Config.res +3 -6
  14. package/src/Config.res.mjs +2 -3
  15. package/src/Core.res +7 -0
  16. package/src/CrossChainState.res +9 -26
  17. package/src/CrossChainState.res.mjs +7 -16
  18. package/src/Ecosystem.res +6 -1
  19. package/src/Envio.res +3 -2
  20. package/src/EventConfigBuilder.res +19 -5
  21. package/src/EventConfigBuilder.res.mjs +6 -4
  22. package/src/EventProcessing.res +26 -0
  23. package/src/EventProcessing.res.mjs +20 -0
  24. package/src/Internal.res +43 -4
  25. package/src/Internal.res.mjs +18 -0
  26. package/src/Main.res +3 -50
  27. package/src/Main.res.mjs +2 -23
  28. package/src/Rollback.res +1 -1
  29. package/src/Rollback.res.mjs +1 -1
  30. package/src/SimulateItems.res +3 -0
  31. package/src/SimulateItems.res.mjs +2 -1
  32. package/src/sources/Evm.res +52 -38
  33. package/src/sources/Evm.res.mjs +48 -36
  34. package/src/sources/Fuel.res +3 -1
  35. package/src/sources/Fuel.res.mjs +1 -0
  36. package/src/sources/HyperFuelSource.res +5 -0
  37. package/src/sources/HyperFuelSource.res.mjs +2 -0
  38. package/src/sources/HyperSync.res +4 -1
  39. package/src/sources/HyperSync.res.mjs +5 -3
  40. package/src/sources/HyperSync.resi +1 -0
  41. package/src/sources/HyperSyncClient.res +9 -78
  42. package/src/sources/HyperSyncClient.res.mjs +1 -22
  43. package/src/sources/HyperSyncSource.res +3 -4
  44. package/src/sources/HyperSyncSource.res.mjs +2 -1
  45. package/src/sources/RpcSource.res +7 -2
  46. package/src/sources/RpcSource.res.mjs +3 -1
  47. package/src/sources/SimulateSource.res +3 -1
  48. package/src/sources/SimulateSource.res.mjs +1 -0
  49. package/src/sources/Source.res +4 -0
  50. package/src/sources/Svm.res +25 -2
  51. package/src/sources/Svm.res.mjs +27 -2
  52. package/src/sources/SvmHyperSyncClient.res +3 -29
  53. package/src/sources/SvmHyperSyncSource.res +60 -84
  54. package/src/sources/SvmHyperSyncSource.res.mjs +59 -78
  55. package/src/sources/TransactionStore.res +111 -0
  56. package/src/sources/TransactionStore.res.mjs +77 -0
  57. package/src/tui/Tui.res +13 -16
  58. package/src/tui/Tui.res.mjs +12 -14
@@ -10,6 +10,7 @@ import * as EvmChain from "./sources/EvmChain.res.mjs";
10
10
  import * as FetchState from "./FetchState.res.mjs";
11
11
  import * as Prometheus from "./Prometheus.res.mjs";
12
12
  import * as EventRouter from "./sources/EventRouter.res.mjs";
13
+ import * as Stdlib_Null from "@rescript/runtime/lib/es6/Stdlib_Null.js";
13
14
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
14
15
  import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
15
16
  import * as SourceManager from "./sources/SourceManager.res.mjs";
@@ -19,6 +20,7 @@ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
19
20
  import * as HyperFuelSource from "./sources/HyperFuelSource.res.mjs";
20
21
  import * as Primitive_float from "@rescript/runtime/lib/es6/Primitive_float.js";
21
22
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
23
+ import * as TransactionStore from "./sources/TransactionStore.res.mjs";
22
24
  import * as SvmHyperSyncSource from "./sources/SvmHyperSyncSource.res.mjs";
23
25
  import * as SafeCheckpointTracking from "./SafeCheckpointTracking.res.mjs";
24
26
 
@@ -36,11 +38,12 @@ function configAddresses(chainConfig) {
36
38
  return addresses;
37
39
  }
38
40
 
39
- function make(chainConfig, fetchState, sourceManager, reorgDetection, committedProgressBlockNumber, safeCheckpointTrackingOpt, numEventsProcessedOpt, timestampCaughtUpToHeadOrEndblockOpt, isProgressAtHeadOpt, logger) {
41
+ function make(chainConfig, fetchState, sourceManager, reorgDetection, committedProgressBlockNumber, safeCheckpointTrackingOpt, numEventsProcessedOpt, timestampCaughtUpToHeadOrEndblockOpt, isProgressAtHeadOpt, transactionFieldMaskOpt, logger) {
40
42
  let safeCheckpointTracking = safeCheckpointTrackingOpt !== undefined ? Primitive_option.valFromOption(safeCheckpointTrackingOpt) : undefined;
41
43
  let numEventsProcessed = numEventsProcessedOpt !== undefined ? numEventsProcessedOpt : 0;
42
44
  let timestampCaughtUpToHeadOrEndblock = timestampCaughtUpToHeadOrEndblockOpt !== undefined ? Primitive_option.valFromOption(timestampCaughtUpToHeadOrEndblockOpt) : undefined;
43
45
  let isProgressAtHead = isProgressAtHeadOpt !== undefined ? isProgressAtHeadOpt : false;
46
+ let transactionFieldMask = transactionFieldMaskOpt !== undefined ? transactionFieldMaskOpt : 0;
44
47
  return {
45
48
  logger: logger,
46
49
  fetchState: fetchState,
@@ -52,7 +55,9 @@ function make(chainConfig, fetchState, sourceManager, reorgDetection, committedP
52
55
  numEventsProcessed: numEventsProcessed,
53
56
  pendingBudget: 0,
54
57
  reorgDetection: reorgDetection,
55
- safeCheckpointTracking: safeCheckpointTracking
58
+ safeCheckpointTracking: safeCheckpointTracking,
59
+ transactionStore: TransactionStore.make(),
60
+ transactionFieldMask: transactionFieldMask
56
61
  };
57
62
  }
58
63
 
@@ -171,7 +176,7 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
171
176
  sources$1 = sources._0;
172
177
  break;
173
178
  }
174
- 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);
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);
175
180
  }
176
181
 
177
182
  function makeFromConfig(chainConfig, config, registrations, knownHeight) {
@@ -195,10 +200,6 @@ function logger(cs) {
195
200
  return cs.logger;
196
201
  }
197
202
 
198
- function fetchState(cs) {
199
- return cs.fetchState;
200
- }
201
-
202
203
  function sourceManager(cs) {
203
204
  return cs.sourceManager;
204
205
  }
@@ -235,11 +236,55 @@ function timestampCaughtUpToHeadOrEndblock(cs) {
235
236
  return cs.timestampCaughtUpToHeadOrEndblock;
236
237
  }
237
238
 
239
+ function knownHeight(cs) {
240
+ return cs.fetchState.knownHeight;
241
+ }
242
+
243
+ function indexingAddresses(cs) {
244
+ return cs.fetchState.indexingAddresses;
245
+ }
246
+
247
+ function bufferSize(cs) {
248
+ return FetchState.bufferSize(cs.fetchState);
249
+ }
250
+
251
+ function bufferReadyCount(cs) {
252
+ return FetchState.bufferReadyCount(cs.fetchState);
253
+ }
254
+
255
+ function getProgressPercentage(cs) {
256
+ return FetchState.getProgressPercentage(cs.fetchState);
257
+ }
258
+
259
+ function getProgressPercentageAt(cs, blockNumber) {
260
+ return FetchState.getProgressPercentageAt(cs.fetchState, blockNumber);
261
+ }
262
+
263
+ function hasReadyItem(cs) {
264
+ if (FetchState.isActivelyIndexing(cs.fetchState)) {
265
+ return FetchState.hasReadyItem(cs.fetchState);
266
+ } else {
267
+ return false;
268
+ }
269
+ }
270
+
271
+ function isReadyToEnterReorgThreshold(cs) {
272
+ return FetchState.isReadyToEnterReorgThreshold(cs.fetchState);
273
+ }
274
+
238
275
  function startFetchingQueries(cs, queries) {
239
276
  FetchState.startFetchingQueries(cs.fetchState, queries);
240
277
  cs.pendingBudget = cs.pendingBudget + Stdlib_Array.reduce(queries, 0, (acc, query) => acc + query.estResponseSize);
241
278
  }
242
279
 
280
+ function getNextQuery(cs, budget) {
281
+ return FetchState.getNextQuery(cs.fetchState, budget, cs.pendingBudget);
282
+ }
283
+
284
+ function dispatch(cs, executeQuery, waitForNewBlock, onNewBlock, action, stateId) {
285
+ return SourceManager.dispatch(cs.sourceManager, cs.fetchState, executeQuery, waitForNewBlock, onNewBlock, action, stateId);
286
+ }
287
+
243
288
  function hasProcessedToEndblock(cs) {
244
289
  let fetchState = cs.fetchState;
245
290
  let committedProgressBlockNumber = cs.committedProgressBlockNumber;
@@ -272,7 +317,30 @@ function isFetchingAtHead(cs) {
272
317
  return FetchState.isFetchingAtHead(cs.fetchState);
273
318
  }
274
319
 
275
- function handleQueryResult(cs, query, newItems, newItemsWithDcs, latestFetchedBlock, knownHeight) {
320
+ function isAtHeadWithoutEndBlock(cs) {
321
+ if (cs.isProgressAtHead) {
322
+ return Stdlib_Option.isNone(cs.fetchState.endBlock);
323
+ } else {
324
+ return false;
325
+ }
326
+ }
327
+
328
+ function materializeBatchItems(cs, items) {
329
+ return TransactionStore.materializeItems(cs.transactionStore, items, cs.transactionFieldMask);
330
+ }
331
+
332
+ function materializePageItems(cs, items, page) {
333
+ if (page !== undefined) {
334
+ return TransactionStore.materializeItems(Primitive_option.valFromOption(page), items, cs.transactionFieldMask);
335
+ } else {
336
+ return Promise.resolve();
337
+ }
338
+ }
339
+
340
+ function handleQueryResult(cs, query, newItems, newItemsWithDcs, latestFetchedBlock, knownHeight, page) {
341
+ if (page !== undefined) {
342
+ cs.transactionStore.merge(Primitive_option.valFromOption(page));
343
+ }
276
344
  let fs = newItemsWithDcs.length !== 0 ? FetchState.registerDynamicContracts(cs.fetchState, newItemsWithDcs) : cs.fetchState;
277
345
  cs.fetchState = FetchState.updateKnownHeight(FetchState.handleQueryResult(fs, query, latestFetchedBlock, newItems), knownHeight);
278
346
  cs.pendingBudget = Primitive_float.max(0, cs.pendingBudget - query.estResponseSize);
@@ -344,6 +412,48 @@ function enterReorgThreshold(cs) {
344
412
  cs.fetchState = FetchState.updateInternal(cs.fetchState, undefined, undefined, undefined, cs.chainConfig.blockLag, undefined);
345
413
  }
346
414
 
415
+ function toChainMetadata(cs) {
416
+ return {
417
+ first_event_block: Stdlib_Null.fromOption(cs.fetchState.firstEventBlock),
418
+ buffer_block: FetchState.bufferBlockNumber(cs.fetchState),
419
+ ready_at: Stdlib_Null.fromOption(cs.timestampCaughtUpToHeadOrEndblock),
420
+ _is_hyper_sync: SourceManager.getActiveSource(cs.sourceManager).poweredByHyperSync
421
+ };
422
+ }
423
+
424
+ function toChainData(cs) {
425
+ return {
426
+ chainId: cs.chainConfig.id,
427
+ poweredByHyperSync: SourceManager.getActiveSource(cs.sourceManager).poweredByHyperSync,
428
+ firstEventBlockNumber: cs.fetchState.firstEventBlock,
429
+ latestProcessedBlock: cs.committedProgressBlockNumber === -1 ? undefined : cs.committedProgressBlockNumber,
430
+ timestampCaughtUpToHeadOrEndblock: cs.timestampCaughtUpToHeadOrEndblock,
431
+ numEventsProcessed: cs.numEventsProcessed,
432
+ latestFetchedBlockNumber: Primitive_int.max(FetchState.bufferBlockNumber(cs.fetchState), 0),
433
+ currentBlockHeight: hasProcessedToEndblock(cs) ? Stdlib_Option.getOr(cs.fetchState.endBlock, cs.fetchState.knownHeight) : cs.fetchState.knownHeight,
434
+ numBatchesFetched: 0,
435
+ startBlock: cs.fetchState.startBlock,
436
+ endBlock: cs.fetchState.endBlock,
437
+ numAddresses: FetchState.numAddresses(cs.fetchState)
438
+ };
439
+ }
440
+
441
+ function toChainBeforeBatch(cs) {
442
+ return {
443
+ fetchState: cs.fetchState,
444
+ reorgDetection: cs.reorgDetection,
445
+ progressBlockNumber: cs.committedProgressBlockNumber,
446
+ sourceBlockNumber: cs.fetchState.knownHeight,
447
+ totalEventsProcessed: cs.numEventsProcessed,
448
+ chainConfig: cs.chainConfig
449
+ };
450
+ }
451
+
452
+ function isReadyToEnterReorgThresholdAfterBatch(cs, batch) {
453
+ let chainAfterBatch = batch.progressedChainsById[cs.fetchState.chainId];
454
+ return FetchState.isReadyToEnterReorgThreshold(chainAfterBatch !== undefined ? chainAfterBatch.fetchState : cs.fetchState);
455
+ }
456
+
347
457
  function advanceAfterBatch(cs, batch, enteringReorgThreshold) {
348
458
  let chainAfterBatch = batch.progressedChainsById[cs.fetchState.chainId];
349
459
  if (chainAfterBatch !== undefined) {
@@ -394,6 +504,7 @@ function applyBatchProgress(cs, batch) {
394
504
  }
395
505
  cs.committedProgressBlockNumber = chainAfterBatch.progressBlockNumber;
396
506
  cs.numEventsProcessed = chainAfterBatch.totalEventsProcessed;
507
+ cs.transactionStore.prune(chainAfterBatch.progressBlockNumber);
397
508
  cs.isProgressAtHead = cs.isProgressAtHead || chainAfterBatch.isProgressAtHeadWhenBatchCreated;
398
509
  let safeCheckpointTracking = cs.safeCheckpointTracking;
399
510
  if (safeCheckpointTracking !== undefined) {
@@ -415,6 +526,7 @@ function rollback(cs, newProgressBlockNumber, eventsProcessedDiff, rollbackTarge
415
526
  if (isReorgChain) {
416
527
  cs.reorgDetection = ReorgDetection.rollbackToValidBlockNumber(cs.reorgDetection, rollbackTargetBlockNumber);
417
528
  cs.fetchState = FetchState.rollback(cs.fetchState, rollbackTargetBlockNumber);
529
+ cs.transactionStore.rollback(rollbackTargetBlockNumber);
418
530
  cs.committedProgressBlockNumber = Primitive_int.min(cs.committedProgressBlockNumber, rollbackTargetBlockNumber);
419
531
  return;
420
532
  } else {
@@ -436,6 +548,7 @@ function rollback(cs, newProgressBlockNumber, eventsProcessedDiff, rollbackTarge
436
548
  cs.safeCheckpointTracking = SafeCheckpointTracking.rollback(safeCheckpointTracking, newProgressBlockNumber);
437
549
  }
438
550
  cs.fetchState = FetchState.rollback(cs.fetchState, newProgressBlockNumber);
551
+ cs.transactionStore.rollback(newProgressBlockNumber);
439
552
  cs.committedProgressBlockNumber = newProgressBlockNumber;
440
553
  cs.numEventsProcessed = newTotalEventsProcessed;
441
554
  }
@@ -446,7 +559,6 @@ export {
446
559
  makeFromConfig,
447
560
  makeFromDbState,
448
561
  logger,
449
- fetchState,
450
562
  sourceManager,
451
563
  chainConfig,
452
564
  reorgDetection,
@@ -457,12 +569,29 @@ export {
457
569
  pendingBudget,
458
570
  startFetchingQueries,
459
571
  timestampCaughtUpToHeadOrEndblock,
572
+ knownHeight,
573
+ indexingAddresses,
574
+ bufferSize,
575
+ bufferReadyCount,
576
+ getProgressPercentage,
577
+ getProgressPercentageAt,
578
+ hasReadyItem,
579
+ isReadyToEnterReorgThreshold,
580
+ getNextQuery,
581
+ dispatch,
582
+ toChainData,
583
+ toChainMetadata,
584
+ toChainBeforeBatch,
585
+ isReadyToEnterReorgThresholdAfterBatch,
460
586
  hasProcessedToEndblock,
461
587
  getHighestBlockBelowThreshold,
462
588
  isActivelyIndexing,
463
589
  isReady,
464
590
  isFetchingAtHead,
591
+ isAtHeadWithoutEndBlock,
465
592
  handleQueryResult,
593
+ materializeBatchItems,
594
+ materializePageItems,
466
595
  registerReorgGuard,
467
596
  prepareReorg,
468
597
  updateKnownHeight,
@@ -15,6 +15,7 @@ let make: (
15
15
  ~numEventsProcessed: float=?,
16
16
  ~timestampCaughtUpToHeadOrEndblock: option<Date.t>=?,
17
17
  ~isProgressAtHead: bool=?,
18
+ ~transactionFieldMask: float=?,
18
19
  ~logger: Pino.t,
19
20
  ) => t
20
21
 
@@ -38,7 +39,6 @@ let makeFromDbState: (
38
39
 
39
40
  // Accessors.
40
41
  let logger: t => Pino.t
41
- let fetchState: t => FetchState.t
42
42
  let sourceManager: t => SourceManager.t
43
43
  let chainConfig: t => Config.chain
44
44
  let reorgDetection: t => ReorgDetection.t
@@ -50,12 +50,55 @@ let pendingBudget: t => float
50
50
  let startFetchingQueries: (t, ~queries: array<FetchState.query>) => unit
51
51
  let timestampCaughtUpToHeadOrEndblock: t => option<Date.t>
52
52
 
53
+ // Fetch-frontier reads.
54
+ let knownHeight: t => int
55
+ let indexingAddresses: t => dict<FetchState.indexingAddress>
56
+ let bufferSize: t => int
57
+ let bufferReadyCount: t => int
58
+ let getProgressPercentage: t => float
59
+ let getProgressPercentageAt: (t, ~blockNumber: int) => float
60
+ let hasReadyItem: t => bool
61
+ let isReadyToEnterReorgThreshold: t => bool
62
+
63
+ // Fetch control.
64
+ let getNextQuery: (t, ~budget: int) => FetchState.nextQuery
65
+ let dispatch: (
66
+ t,
67
+ ~executeQuery: FetchState.query => promise<unit>,
68
+ ~waitForNewBlock: (~knownHeight: int) => promise<int>,
69
+ ~onNewBlock: (~knownHeight: int) => unit,
70
+ ~action: FetchState.nextQuery,
71
+ ~stateId: int,
72
+ ) => promise<unit>
73
+
74
+ // Views.
75
+ type chainData = {
76
+ chainId: float,
77
+ poweredByHyperSync: bool,
78
+ firstEventBlockNumber: option<int>,
79
+ latestProcessedBlock: option<int>,
80
+ timestampCaughtUpToHeadOrEndblock: option<Date.t>,
81
+ numEventsProcessed: float,
82
+ latestFetchedBlockNumber: int,
83
+ @as("currentBlockHeight")
84
+ knownHeight: int,
85
+ numBatchesFetched: int,
86
+ startBlock: int,
87
+ endBlock: option<int>,
88
+ numAddresses: int,
89
+ }
90
+ let toChainData: t => chainData
91
+ let toChainMetadata: t => InternalTable.Chains.metaFields
92
+ let toChainBeforeBatch: t => Batch.chainBeforeBatch
93
+ let isReadyToEnterReorgThresholdAfterBatch: (t, ~batch: Batch.t) => bool
94
+
53
95
  // Derived (pure).
54
96
  let hasProcessedToEndblock: t => bool
55
97
  let getHighestBlockBelowThreshold: t => int
56
98
  let isActivelyIndexing: t => bool
57
99
  let isReady: t => bool
58
100
  let isFetchingAtHead: t => bool
101
+ let isAtHeadWithoutEndBlock: t => bool
59
102
 
60
103
  // State transitions. The chain state is mutated only through these.
61
104
  let handleQueryResult: (
@@ -65,7 +108,14 @@ let handleQueryResult: (
65
108
  ~newItemsWithDcs: array<Internal.item>,
66
109
  ~latestFetchedBlock: FetchState.blockNumberAndTimestamp,
67
110
  ~knownHeight: int,
111
+ ~transactionStore: option<TransactionStore.t>,
68
112
  ) => unit
113
+ let materializeBatchItems: (t, ~items: array<Internal.item>) => promise<unit>
114
+ let materializePageItems: (
115
+ t,
116
+ ~items: array<Internal.item>,
117
+ ~page: option<TransactionStore.t>,
118
+ ) => promise<unit>
69
119
  let registerReorgGuard: (
70
120
  t,
71
121
  ~blockHashes: array<ReorgDetection.blockData>,
package/src/Config.res CHANGED
@@ -195,9 +195,8 @@ let svmEventDescriptorSchema = S.schema(s =>
195
195
  {
196
196
  "discriminator": s.matches(S.option(S.string)),
197
197
  "discriminatorByteLen": s.matches(S.int),
198
- "includeTransaction": s.matches(S.bool),
198
+ "transactionFields": s.matches(S.array(Internal.svmTransactionFieldSchema)),
199
199
  "includeLogs": s.matches(S.bool),
200
- "includeTokenBalances": s.matches(S.bool),
201
200
  "accountFilters": s.matches(
202
201
  S.option(
203
202
  S.array(
@@ -704,9 +703,8 @@ let fromPublic = (publicConfigJson: JSON.t) => {
704
703
  "svm": option<{
705
704
  "discriminator": option<string>,
706
705
  "discriminatorByteLen": int,
707
- "includeTransaction": bool,
706
+ "transactionFields": array<Internal.svmTransactionField>,
708
707
  "includeLogs": bool,
709
- "includeTokenBalances": bool,
710
708
  "accountFilters": option<
711
709
  array<array<{"position": int, "values": array<string>}>>,
712
710
  >,
@@ -740,9 +738,8 @@ let fromPublic = (publicConfigJson: JSON.t) => {
740
738
  ~programId,
741
739
  ~discriminator=svm["discriminator"],
742
740
  ~discriminatorByteLen=svm["discriminatorByteLen"],
743
- ~includeTransaction=svm["includeTransaction"],
741
+ ~transactionFields=svm["transactionFields"],
744
742
  ~includeLogs=svm["includeLogs"],
745
- ~includeTokenBalances=svm["includeTokenBalances"],
746
743
  ~accountFilters,
747
744
  ~isInner=svm["isInner"],
748
745
  ~isWildcard=false,
@@ -111,9 +111,8 @@ let publicConfigChainSchema = S$RescriptSchema.schema(s => ({
111
111
  let svmEventDescriptorSchema = S$RescriptSchema.schema(s => ({
112
112
  discriminator: s.m(S$RescriptSchema.option(S$RescriptSchema.string)),
113
113
  discriminatorByteLen: s.m(S$RescriptSchema.int),
114
- includeTransaction: s.m(S$RescriptSchema.bool),
114
+ transactionFields: s.m(S$RescriptSchema.array(Internal.svmTransactionFieldSchema)),
115
115
  includeLogs: s.m(S$RescriptSchema.bool),
116
- includeTokenBalances: s.m(S$RescriptSchema.bool),
117
116
  accountFilters: s.m(S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.schema(s => ({
118
117
  position: s.m(S$RescriptSchema.int),
119
118
  values: s.m(S$RescriptSchema.array(S$RescriptSchema.string))
@@ -518,7 +517,7 @@ function fromPublic(publicConfigJson) {
518
517
  position: af.position,
519
518
  values: af.values
520
519
  })));
521
- return EventConfigBuilder.buildSvmInstructionEventConfig(contractName, eventName, programId, svm.discriminator, svm.discriminatorByteLen, svm.includeTransaction, svm.includeLogs, svm.includeTokenBalances, accountFilters, svm.isInner, false, undefined, undefined, Stdlib_Option.getOr(svm.accounts, []), Stdlib_Option.getOr(svm.args, null), svmDefinedTypes, startBlock);
520
+ return EventConfigBuilder.buildSvmInstructionEventConfig(contractName, eventName, programId, svm.discriminator, svm.discriminatorByteLen, svm.includeLogs, svm.transactionFields, accountFilters, svm.isInner, false, undefined, undefined, Stdlib_Option.getOr(svm.accounts, []), Stdlib_Option.getOr(svm.args, null), svmDefinedTypes, startBlock);
522
521
  }
523
522
  });
524
523
  } else {
package/src/Core.res CHANGED
@@ -9,6 +9,7 @@ type evmRpcClientCtor
9
9
  type evmDecoderCtor
10
10
  type svmHypersyncClientCtor
11
11
  type hyperfuelClientCtor
12
+ type transactionStoreCtor
12
13
 
13
14
  type addon = {
14
15
  getConfigJson: (~configPath: Null.t<string>, ~directory: Null.t<string>) => string,
@@ -23,6 +24,12 @@ type addon = {
23
24
  svmHypersyncClient: svmHypersyncClientCtor,
24
25
  @as("HyperfuelClient")
25
26
  hyperfuelClient: hyperfuelClientCtor,
27
+ @as("TransactionStore")
28
+ transactionStore: transactionStoreCtor,
29
+ // Ordered transaction-field names exposed for the field-code contract test
30
+ // (the ReScript `transactionFields` arrays must match the Rust ordinals).
31
+ evmTransactionFieldNames: unit => array<string>,
32
+ svmTransactionFieldNames: unit => array<string>,
26
33
  }
27
34
 
28
35
  @module("node:module") external createRequire: string => {..} = "createRequire"
@@ -57,7 +57,7 @@ let totalReadyCount = (crossChainState: t) => {
57
57
  let total = ref(0)
58
58
  for i in 0 to crossChainState.chainIds->Array.length - 1 {
59
59
  let cs = crossChainState->getChainState(crossChainState.chainIds->Array.getUnsafe(i))
60
- total := total.contents + cs->ChainState.fetchState->FetchState.bufferReadyCount
60
+ total := total.contents + cs->ChainState.bufferReadyCount
61
61
  }
62
62
  total.contents
63
63
  }
@@ -65,9 +65,7 @@ let totalReadyCount = (crossChainState: t) => {
65
65
  // --- Derived (pure). ---
66
66
 
67
67
  let nextItemIsNone = (crossChainState: t): bool =>
68
- !Batch.hasReadyItem(
69
- crossChainState.chainStates->Dict.valuesToArray->Array.map(ChainState.fetchState),
70
- )
68
+ !(crossChainState.chainStates->Dict.valuesToArray->Array.some(ChainState.hasReadyItem))
71
69
 
72
70
  let getSafeCheckpointId = (crossChainState: t) => {
73
71
  let result: ref<option<bigint>> = ref(None)
@@ -79,7 +77,7 @@ let getSafeCheckpointId = (crossChainState: t) => {
79
77
  | Some(safeCheckpointTracking) =>
80
78
  let safeCheckpointId =
81
79
  safeCheckpointTracking->SafeCheckpointTracking.getSafeCheckpointId(
82
- ~sourceBlockNumber=(cs->ChainState.fetchState).knownHeight,
80
+ ~sourceBlockNumber=cs->ChainState.knownHeight,
83
81
  )
84
82
  switch result.contents {
85
83
  | None => result := Some(safeCheckpointId)
@@ -111,16 +109,9 @@ let createBatch = (
111
109
  // in an append-only ClickHouse insert.
112
110
  isRollback ? 1n : 0n,
113
111
  ),
114
- ~chainsBeforeBatch=crossChainState.chainStates->Utils.Dict.mapValues((
115
- cs
116
- ): Batch.chainBeforeBatch => {
117
- fetchState: cs->ChainState.fetchState,
118
- progressBlockNumber: cs->ChainState.committedProgressBlockNumber,
119
- totalEventsProcessed: cs->ChainState.numEventsProcessed,
120
- sourceBlockNumber: (cs->ChainState.fetchState).knownHeight,
121
- reorgDetection: cs->ChainState.reorgDetection,
122
- chainConfig: cs->ChainState.chainConfig,
123
- }),
112
+ ~chainsBeforeBatch=crossChainState.chainStates->Utils.Dict.mapValues(
113
+ ChainState.toChainBeforeBatch,
114
+ ),
124
115
  ~batchSizeTarget,
125
116
  )
126
117
  }
@@ -184,10 +175,7 @@ let priorityOrder = (crossChainState: t) =>
184
175
  crossChainState.chainStates
185
176
  ->Dict.valuesToArray
186
177
  ->Array.toSorted((a, b) =>
187
- Float.compare(
188
- a->ChainState.fetchState->FetchState.getProgressPercentage,
189
- b->ChainState.fetchState->FetchState.getProgressPercentage,
190
- )
178
+ Float.compare(a->ChainState.getProgressPercentage, b->ChainState.getProgressPercentage)
191
179
  )
192
180
 
193
181
  // In-flight estimated items across every chain — the live draw against
@@ -235,11 +223,7 @@ let checkAndFetch = async (
235
223
  for i in 0 to chainIds->Array.length - 1 {
236
224
  let chainId = chainIds->Array.getUnsafe(i)
237
225
  let cs = crossChainState->getChainState(chainId)
238
- let fetchState = cs->ChainState.fetchState
239
- switch fetchState->FetchState.getNextQuery(
240
- ~budget=remaining,
241
- ~chainPendingBudget=cs->ChainState.pendingBudget,
242
- ) {
226
+ switch cs->ChainState.getNextQuery(~budget=remaining) {
243
227
  | (WaitingForNewBlock | NothingToQuery) as action =>
244
228
  actionByChain->Utils.Dict.setByInt(chainId, action)
245
229
  | Ready(queries) =>
@@ -247,8 +231,7 @@ let checkAndFetch = async (
247
231
  actionByChain->Utils.Dict.setByInt(chainId, FetchState.NothingToQuery)
248
232
  queries->Array.forEach(query => {
249
233
  query.chainId = chainId
250
- query.progress =
251
- fetchState->FetchState.getProgressPercentageAt(~blockNumber=query.fromBlock)
234
+ query.progress = cs->ChainState.getProgressPercentageAt(~blockNumber=query.fromBlock)
252
235
  candidates->Array.push(query)
253
236
  })
254
237
  }
@@ -6,7 +6,6 @@ import * as Utils from "./Utils.res.mjs";
6
6
  import * as Logging from "./Logging.res.mjs";
7
7
  import * as ChainMap from "./ChainMap.res.mjs";
8
8
  import * as ChainState from "./ChainState.res.mjs";
9
- import * as FetchState from "./FetchState.res.mjs";
10
9
  import * as Prometheus from "./Prometheus.res.mjs";
11
10
  import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
12
11
  import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
@@ -53,13 +52,13 @@ function totalReadyCount(crossChainState) {
53
52
  let total = 0;
54
53
  for (let i = 0, i_finish = crossChainState.chainIds.length; i < i_finish; ++i) {
55
54
  let cs = crossChainState.chainStates[crossChainState.chainIds[i]];
56
- total = total + FetchState.bufferReadyCount(ChainState.fetchState(cs)) | 0;
55
+ total = total + ChainState.bufferReadyCount(cs) | 0;
57
56
  }
58
57
  return total;
59
58
  }
60
59
 
61
60
  function nextItemIsNone(crossChainState) {
62
- return !Batch.hasReadyItem(Object.values(crossChainState.chainStates).map(ChainState.fetchState));
61
+ return !Object.values(crossChainState.chainStates).some(ChainState.hasReadyItem);
63
62
  }
64
63
 
65
64
  function getSafeCheckpointId(crossChainState) {
@@ -68,7 +67,7 @@ function getSafeCheckpointId(crossChainState) {
68
67
  let cs = crossChainState.chainStates[crossChainState.chainIds[i]];
69
68
  let safeCheckpointTracking = ChainState.safeCheckpointTracking(cs);
70
69
  if (safeCheckpointTracking !== undefined) {
71
- let safeCheckpointId = SafeCheckpointTracking.getSafeCheckpointId(safeCheckpointTracking, ChainState.fetchState(cs).knownHeight);
70
+ let safeCheckpointId = SafeCheckpointTracking.getSafeCheckpointId(safeCheckpointTracking, ChainState.knownHeight(cs));
72
71
  let current = result;
73
72
  if (!(current !== undefined && safeCheckpointId >= current)) {
74
73
  result = safeCheckpointId;
@@ -84,14 +83,7 @@ function getSafeCheckpointId(crossChainState) {
84
83
  function createBatch(crossChainState, processedCheckpointId, batchSizeTarget, isRollback) {
85
84
  return Batch.make(processedCheckpointId + (
86
85
  isRollback ? 1n : 0n
87
- ), Utils.Dict.mapValues(crossChainState.chainStates, cs => ({
88
- fetchState: ChainState.fetchState(cs),
89
- reorgDetection: ChainState.reorgDetection(cs),
90
- progressBlockNumber: ChainState.committedProgressBlockNumber(cs),
91
- sourceBlockNumber: ChainState.fetchState(cs).knownHeight,
92
- totalEventsProcessed: ChainState.numEventsProcessed(cs),
93
- chainConfig: ChainState.chainConfig(cs)
94
- })), batchSizeTarget, crossChainState.isInReorgThreshold);
86
+ ), Utils.Dict.mapValues(crossChainState.chainStates, ChainState.toChainBeforeBatch), batchSizeTarget, crossChainState.isInReorgThreshold);
95
87
  }
96
88
 
97
89
  function enterReorgThreshold(crossChainState) {
@@ -131,7 +123,7 @@ function applyBatchProgress(crossChainState, batch) {
131
123
  }
132
124
 
133
125
  function priorityOrder(crossChainState) {
134
- return Object.values(crossChainState.chainStates).toSorted((a, b) => Primitive_float.compare(FetchState.getProgressPercentage(ChainState.fetchState(a)), FetchState.getProgressPercentage(ChainState.fetchState(b))));
126
+ return Object.values(crossChainState.chainStates).toSorted((a, b) => Primitive_float.compare(ChainState.getProgressPercentage(a), ChainState.getProgressPercentage(b)));
135
127
  }
136
128
 
137
129
  function totalReservedSize(crossChainState) {
@@ -155,15 +147,14 @@ async function checkAndFetch(crossChainState, dispatchChain) {
155
147
  for (let i = 0, i_finish = chainIds.length; i < i_finish; ++i) {
156
148
  let chainId = chainIds[i];
157
149
  let cs = crossChainState.chainStates[chainId];
158
- let fetchState = ChainState.fetchState(cs);
159
- let action = FetchState.getNextQuery(fetchState, remaining, ChainState.pendingBudget(cs));
150
+ let action = ChainState.getNextQuery(cs, remaining);
160
151
  if (typeof action !== "object") {
161
152
  actionByChain[chainId] = action;
162
153
  } else {
163
154
  actionByChain[chainId] = "NothingToQuery";
164
155
  action._0.forEach(query => {
165
156
  query.chainId = chainId;
166
- query.progress = FetchState.getProgressPercentageAt(fetchState, query.fromBlock);
157
+ query.progress = ChainState.getProgressPercentageAt(cs, query.fromBlock);
167
158
  candidates.push(query);
168
159
  });
169
160
  }
package/src/Ecosystem.res CHANGED
@@ -33,8 +33,13 @@ type t = {
33
33
  loggers (see `getItemLogger`). */
34
34
  logger: Pino.t,
35
35
  /** Materialise the user-facing event handed to handlers and contract
36
- registration from an item's opaque payload. */
36
+ registration from an item's opaque payload. `event.transaction` is written
37
+ onto the payload at batch prep (HyperSync) or inline (RPC/simulate). */
37
38
  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,
38
43
  /** Build the per-item child logger for an event item, with
39
44
  ecosystem-specific log fields (EVM/Fuel: contract/event/address; SVM:
40
45
  program/instruction/programId). Closes over the injected logger. */
package/src/Envio.res CHANGED
@@ -48,6 +48,7 @@ type svmTokenBalance = {
48
48
  }
49
49
 
50
50
  type svmTransaction = {
51
+ transactionIndex?: int,
51
52
  signatures: array<string>,
52
53
  feePayer?: SvmTypes.Pubkey.t,
53
54
  success?: bool,
@@ -104,8 +105,8 @@ type svmInstruction = {
104
105
  d8?: string,
105
106
  /** Borsh-decoded params view. See [[svmInstructionParams]]. */
106
107
  params?: svmInstructionParams,
107
- /** Parent transaction. Absent when the per-instruction
108
- `include_transaction` flag is `false`. */
108
+ /** Parent transaction. Carries only the fields selected via
109
+ `field_selection.transaction_fields`; absent when none are selected. */
109
110
  transaction?: svmTransaction,
110
111
  /** Program log entries scoped to this instruction. Absent when the
111
112
  per-instruction `include_logs` flag is `false`. */
@@ -306,7 +306,14 @@ let resolveFieldSelection = (
306
306
  | Some(fields) => Utils.Set.fromArray(fields)
307
307
  | None => globalTransactionFieldsSet
308
308
  }
309
- (selectedBlockFields, selectedTransactionFields)
309
+ // The base eventConfig stores these as a string set (field names match the
310
+ // typed variants at runtime).
311
+ (
312
+ selectedBlockFields,
313
+ selectedTransactionFields->(
314
+ Utils.magic: Utils.Set.t<Internal.evmTransactionField> => Utils.Set.t<string>
315
+ ),
316
+ )
310
317
  }
311
318
 
312
319
  // ============== Client-side address filter ==============
@@ -427,9 +434,8 @@ let buildSvmInstructionEventConfig = (
427
434
  ~programId: SvmTypes.Pubkey.t,
428
435
  ~discriminator: option<string>,
429
436
  ~discriminatorByteLen: int,
430
- ~includeTransaction: bool,
431
437
  ~includeLogs: bool,
432
- ~includeTokenBalances: bool,
438
+ ~transactionFields: array<Internal.svmTransactionField>=[],
433
439
  ~accountFilters: array<Internal.svmAccountFilterGroup>,
434
440
  ~isInner: option<bool>,
435
441
  ~isWildcard: bool,
@@ -444,6 +450,13 @@ let buildSvmInstructionEventConfig = (
444
450
  S.json(~validate=false)
445
451
  ->Utils.Schema.coerceToJsonPgType
446
452
  ->(Utils.magic: S.t<JSON.t> => S.t<Internal.eventParams>)
453
+
454
+ // The base eventConfig stores these as a string set (field names match the
455
+ // typed variants at runtime).
456
+ let selectedTransactionFields =
457
+ Utils.Set.fromArray(transactionFields)->(
458
+ Utils.magic: Utils.Set.t<Internal.svmTransactionField> => Utils.Set.t<string>
459
+ )
447
460
  {
448
461
  id: switch discriminator {
449
462
  | Some(d) => d
@@ -462,9 +475,8 @@ let buildSvmInstructionEventConfig = (
462
475
  programId,
463
476
  discriminator,
464
477
  discriminatorByteLen,
465
- includeTransaction,
466
478
  includeLogs,
467
- includeTokenBalances,
479
+ selectedTransactionFields,
468
480
  accountFilters,
469
481
  isInner,
470
482
  accounts,
@@ -530,6 +542,8 @@ let buildFuelEventConfig = (
530
542
  filterByAddresses: false,
531
543
  dependsOnAddresses: !isWildcard,
532
544
  startBlock,
545
+ // Fuel keeps the transaction inline on the payload; nothing to materialise.
546
+ selectedTransactionFields: Utils.Set.make(),
533
547
  kind: fuelKind,
534
548
  }
535
549
  }