envio 3.3.0-alpha.0 → 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 (73) 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 +22 -13
  7. package/src/ChainFetching.res.mjs +13 -13
  8. package/src/ChainMetadata.res +1 -11
  9. package/src/ChainMetadata.res.mjs +1 -10
  10. package/src/ChainState.res +176 -2
  11. package/src/ChainState.res.mjs +153 -9
  12. package/src/ChainState.resi +53 -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 +99 -91
  17. package/src/CrossChainState.res.mjs +72 -57
  18. package/src/CrossChainState.resi +1 -9
  19. package/src/Ecosystem.res +6 -1
  20. package/src/Env.res +0 -7
  21. package/src/Env.res.mjs +0 -6
  22. package/src/Envio.res +3 -2
  23. package/src/EventConfigBuilder.res +19 -5
  24. package/src/EventConfigBuilder.res.mjs +6 -4
  25. package/src/EventProcessing.res +26 -0
  26. package/src/EventProcessing.res.mjs +20 -0
  27. package/src/FetchState.res +92 -22
  28. package/src/FetchState.res.mjs +87 -14
  29. package/src/IndexerLoop.res +2 -3
  30. package/src/IndexerLoop.res.mjs +1 -1
  31. package/src/IndexerState.res +0 -8
  32. package/src/IndexerState.res.mjs +4 -8
  33. package/src/IndexerState.resi +0 -4
  34. package/src/Internal.res +43 -4
  35. package/src/Internal.res.mjs +18 -0
  36. package/src/Main.res +3 -50
  37. package/src/Main.res.mjs +3 -24
  38. package/src/Prometheus.res +0 -11
  39. package/src/Prometheus.res.mjs +83 -98
  40. package/src/Rollback.res +1 -1
  41. package/src/Rollback.res.mjs +1 -1
  42. package/src/SimulateItems.res +3 -0
  43. package/src/SimulateItems.res.mjs +2 -1
  44. package/src/sources/Evm.res +52 -38
  45. package/src/sources/Evm.res.mjs +48 -36
  46. package/src/sources/Fuel.res +3 -1
  47. package/src/sources/Fuel.res.mjs +1 -0
  48. package/src/sources/HyperFuelSource.res +5 -0
  49. package/src/sources/HyperFuelSource.res.mjs +2 -0
  50. package/src/sources/HyperSync.res +4 -1
  51. package/src/sources/HyperSync.res.mjs +5 -3
  52. package/src/sources/HyperSync.resi +1 -0
  53. package/src/sources/HyperSyncClient.res +9 -78
  54. package/src/sources/HyperSyncClient.res.mjs +1 -22
  55. package/src/sources/HyperSyncSource.res +3 -4
  56. package/src/sources/HyperSyncSource.res.mjs +2 -1
  57. package/src/sources/RpcSource.res +7 -2
  58. package/src/sources/RpcSource.res.mjs +3 -1
  59. package/src/sources/SimulateSource.res +3 -1
  60. package/src/sources/SimulateSource.res.mjs +1 -0
  61. package/src/sources/Source.res +4 -0
  62. package/src/sources/SourceManager.res +9 -8
  63. package/src/sources/SourceManager.res.mjs +21 -26
  64. package/src/sources/SourceManager.resi +2 -3
  65. package/src/sources/Svm.res +25 -2
  66. package/src/sources/Svm.res.mjs +27 -2
  67. package/src/sources/SvmHyperSyncClient.res +3 -29
  68. package/src/sources/SvmHyperSyncSource.res +60 -84
  69. package/src/sources/SvmHyperSyncSource.res.mjs +59 -78
  70. package/src/sources/TransactionStore.res +111 -0
  71. package/src/sources/TransactionStore.res.mjs +77 -0
  72. package/src/tui/Tui.res +13 -16
  73. package/src/tui/Tui.res.mjs +12 -14
@@ -11,8 +11,36 @@ type t = {
11
11
  mutable timestampCaughtUpToHeadOrEndblock: option<Date.t>,
12
12
  mutable committedProgressBlockNumber: int,
13
13
  mutable numEventsProcessed: float,
14
+ // Running sum of in-flight queries' estResponseSize, kept here so the
15
+ // scheduler doesn't re-sum pending queries on every tick. Incremented when
16
+ // queries are dispatched, decremented as their responses land.
17
+ mutable pendingBudget: float,
14
18
  mutable reorgDetection: ReorgDetection.t,
15
19
  mutable safeCheckpointTracking: option<SafeCheckpointTracking.t>,
20
+ // Holds this chain's transactions (kept in Rust) keyed by (blockNumber,
21
+ // transactionIndex). Fetch responses merge their page in; entries are pruned
22
+ // as the chain progresses and dropped above the target on rollback.
23
+ transactionStore: TransactionStore.t,
24
+ // Bitmask of the transaction fields the store materialises at batch prep.
25
+ transactionFieldMask: float,
26
+ }
27
+
28
+ // Per-chain shape returned by the status API.
29
+ type chainData = {
30
+ chainId: float,
31
+ poweredByHyperSync: bool,
32
+ firstEventBlockNumber: option<int>,
33
+ latestProcessedBlock: option<int>,
34
+ timestampCaughtUpToHeadOrEndblock: option<Date.t>,
35
+ numEventsProcessed: float,
36
+ latestFetchedBlockNumber: int,
37
+ // Need this for API backwards compatibility
38
+ @as("currentBlockHeight")
39
+ knownHeight: int,
40
+ numBatchesFetched: int,
41
+ startBlock: int,
42
+ endBlock: option<int>,
43
+ numAddresses: int,
16
44
  }
17
45
 
18
46
  let configAddresses = (chainConfig: Config.chain): array<Internal.indexingAddress> => {
@@ -39,6 +67,7 @@ let make = (
39
67
  ~numEventsProcessed=0.,
40
68
  ~timestampCaughtUpToHeadOrEndblock=None,
41
69
  ~isProgressAtHead=false,
70
+ ~transactionFieldMask=0.,
42
71
  ~logger: Pino.t,
43
72
  ): t => {
44
73
  logger,
@@ -49,8 +78,11 @@ let make = (
49
78
  timestampCaughtUpToHeadOrEndblock,
50
79
  committedProgressBlockNumber,
51
80
  numEventsProcessed,
81
+ pendingBudget: 0.,
52
82
  reorgDetection,
53
83
  safeCheckpointTracking,
84
+ transactionStore: TransactionStore.make(),
85
+ transactionFieldMask,
54
86
  }
55
87
 
56
88
  let makeInternal = (
@@ -296,6 +328,7 @@ let makeInternal = (
296
328
  ~committedProgressBlockNumber=progressBlockNumber,
297
329
  ~timestampCaughtUpToHeadOrEndblock,
298
330
  ~numEventsProcessed,
331
+ ~transactionFieldMask=config.ecosystem.transactionFieldMask(eventConfigs),
299
332
  ~logger,
300
333
  )
301
334
  }
@@ -372,7 +405,6 @@ let makeFromDbState = (
372
405
  // --- Read accessors. ---
373
406
 
374
407
  let logger = (cs: t) => cs.logger
375
- let fetchState = (cs: t) => cs.fetchState
376
408
  let sourceManager = (cs: t) => cs.sourceManager
377
409
  let chainConfig = (cs: t) => cs.chainConfig
378
410
  let reorgDetection = (cs: t) => cs.reorgDetection
@@ -380,8 +412,62 @@ let safeCheckpointTracking = (cs: t) => cs.safeCheckpointTracking
380
412
  let isProgressAtHead = (cs: t) => cs.isProgressAtHead
381
413
  let committedProgressBlockNumber = (cs: t) => cs.committedProgressBlockNumber
382
414
  let numEventsProcessed = (cs: t) => cs.numEventsProcessed
415
+ let pendingBudget = (cs: t) => cs.pendingBudget
383
416
  let timestampCaughtUpToHeadOrEndblock = (cs: t) => cs.timestampCaughtUpToHeadOrEndblock
384
417
 
418
+ // Fetch-frontier reads. The FetchState is owned here; callers go through these
419
+ // rather than reaching into it.
420
+ let knownHeight = (cs: t) => cs.fetchState.knownHeight
421
+ let indexingAddresses = (cs: t) => cs.fetchState.indexingAddresses
422
+ let bufferSize = (cs: t) => cs.fetchState->FetchState.bufferSize
423
+ let bufferReadyCount = (cs: t) => cs.fetchState->FetchState.bufferReadyCount
424
+ let getProgressPercentage = (cs: t) => cs.fetchState->FetchState.getProgressPercentage
425
+ let getProgressPercentageAt = (cs: t, ~blockNumber) =>
426
+ cs.fetchState->FetchState.getProgressPercentageAt(~blockNumber)
427
+ let hasReadyItem = (cs: t) =>
428
+ cs.fetchState->FetchState.isActivelyIndexing && cs.fetchState->FetchState.hasReadyItem
429
+ let isReadyToEnterReorgThreshold = (cs: t) => cs.fetchState->FetchState.isReadyToEnterReorgThreshold
430
+
431
+ // Mark queries as in flight and reserve their estimated size against the shared
432
+ // buffer budget in one step, so the counter stays in sync with the pending
433
+ // queries it tracks.
434
+ let startFetchingQueries = (cs: t, ~queries: array<FetchState.query>) => {
435
+ cs.fetchState->FetchState.startFetchingQueries(~queries)
436
+ cs.pendingBudget =
437
+ cs.pendingBudget +. queries->Array.reduce(0., (acc, query) => acc +. query.estResponseSize)
438
+ }
439
+
440
+ // Drop every in-flight query and release their reservations together, keeping
441
+ // pendingBudget coupled to the pending queries it tracks.
442
+ let resetPendingQueries = (cs: t) => {
443
+ cs.fetchState = cs.fetchState->FetchState.resetPendingQueries
444
+ cs.pendingBudget = 0.
445
+ }
446
+
447
+ // Propose the chain's candidate queries against the shared buffer budget,
448
+ // accounting for this chain's own in-flight reservations.
449
+ let getNextQuery = (cs: t, ~budget) =>
450
+ cs.fetchState->FetchState.getNextQuery(~budget, ~chainPendingBudget=cs.pendingBudget)
451
+
452
+ // Run a fetch tick for this chain against its sources, feeding the owned fetch
453
+ // frontier to the source manager.
454
+ let dispatch = (
455
+ cs: t,
456
+ ~executeQuery,
457
+ ~waitForNewBlock,
458
+ ~onNewBlock,
459
+ ~action: FetchState.nextQuery,
460
+ ~stateId,
461
+ ) =>
462
+ cs.sourceManager->SourceManager.dispatch(
463
+ ~fetchState=cs.fetchState,
464
+ ~executeQuery,
465
+ ~waitForNewBlock,
466
+ ~onNewBlock,
467
+ ~action,
468
+ ~stateId,
469
+ )
470
+
385
471
  // --- Derived (pure). ---
386
472
 
387
473
  let hasProcessedToEndblock = (cs: t) => {
@@ -404,11 +490,34 @@ let isReady = (cs: t) => cs.timestampCaughtUpToHeadOrEndblock !== None
404
490
  // True once the fetch frontier has reached the head/endBlock for this chain.
405
491
  let isFetchingAtHead = (cs: t) => cs.fetchState->FetchState.isFetchingAtHead
406
492
 
493
+ // Reached head on a chain with no configured endBlock — used by auto-exit to
494
+ // detect that no events were found in the start..head range.
495
+ let isAtHeadWithoutEndBlock = (cs: t) =>
496
+ cs.isProgressAtHead && cs.fetchState.endBlock->Option.isNone
497
+
407
498
  // --- State transitions. The chain state is mutated only through these; each
408
499
  // owns a cohesive update so callers don't juggle individual fields. ---
409
500
 
410
501
  // Apply a fetch response: register any new dynamic contracts, append the items
411
502
  // to the buffer and advance the known head.
503
+ // Materialise the chain store's selected transaction fields onto a batch's
504
+ // items at batch prep (the persistent-store path).
505
+ let materializeBatchItems = (cs: t, ~items: array<Internal.item>) =>
506
+ cs.transactionStore->TransactionStore.materializeItems(~items, ~mask=cs.transactionFieldMask)
507
+
508
+ // Materialise a fetch-response page's transactions onto its items before
509
+ // contract-register handlers read them. `None` pages (RPC/Fuel/Simulate keep the
510
+ // transaction inline) are a no-op.
511
+ let materializePageItems = (
512
+ cs: t,
513
+ ~items: array<Internal.item>,
514
+ ~page: option<TransactionStore.t>,
515
+ ) =>
516
+ switch page {
517
+ | Some(store) => store->TransactionStore.materializeItems(~items, ~mask=cs.transactionFieldMask)
518
+ | None => Promise.resolve()
519
+ }
520
+
412
521
  let handleQueryResult = (
413
522
  cs: t,
414
523
  ~query: FetchState.query,
@@ -416,7 +525,15 @@ let handleQueryResult = (
416
525
  ~newItemsWithDcs,
417
526
  ~latestFetchedBlock,
418
527
  ~knownHeight,
528
+ ~transactionStore as page: option<TransactionStore.t>,
419
529
  ) => {
530
+ // Merge this response's page into the chain store in lockstep with appending
531
+ // its items to the buffer. Inline-transaction sources contribute no page.
532
+ switch page {
533
+ | Some(page) => cs.transactionStore->TransactionStore.merge(page)
534
+ | None => ()
535
+ }
536
+
420
537
  let fs = switch newItemsWithDcs {
421
538
  | [] => cs.fetchState
422
539
  | _ => cs.fetchState->FetchState.registerDynamicContracts(newItemsWithDcs)
@@ -426,6 +543,9 @@ let handleQueryResult = (
426
543
  fs
427
544
  ->FetchState.handleQueryResult(~query, ~latestFetchedBlock, ~newItems)
428
545
  ->FetchState.updateKnownHeight(~knownHeight)
546
+
547
+ // The query is no longer in flight, so release its reservation.
548
+ cs.pendingBudget = Pervasives.max(0., cs.pendingBudget -. query.estResponseSize)
429
549
  }
430
550
 
431
551
  // Run reorg detection against a fetch response and commit the updated guard.
@@ -447,7 +567,7 @@ let prepareReorg = (cs: t, ~eventsProcessedDiff) => {
447
567
  | Some(diff) => cs.numEventsProcessed = cs.numEventsProcessed +. diff
448
568
  | None => ()
449
569
  }
450
- cs.fetchState = cs.fetchState->FetchState.resetPendingQueries
570
+ cs->resetPendingQueries
451
571
  }
452
572
 
453
573
  let updateKnownHeight = (cs: t, ~knownHeight) =>
@@ -466,6 +586,56 @@ let setEndBlockToFirstEvent = (cs: t, ~blockNumber) =>
466
586
  let enterReorgThreshold = (cs: t) =>
467
587
  cs.fetchState = cs.fetchState->FetchState.updateInternal(~blockLag=cs.chainConfig.blockLag)
468
588
 
589
+ // Snapshot the chain's metadata fields for staging into the chains table.
590
+ let toChainMetadata = (cs: t): InternalTable.Chains.metaFields => {
591
+ firstEventBlockNumber: cs.fetchState.firstEventBlock->Null.fromOption,
592
+ isHyperSync: (cs.sourceManager->SourceManager.getActiveSource).poweredByHyperSync,
593
+ latestFetchedBlockNumber: cs.fetchState->FetchState.bufferBlockNumber,
594
+ timestampCaughtUpToHeadOrEndblock: cs.timestampCaughtUpToHeadOrEndblock->Null.fromOption,
595
+ }
596
+
597
+ // Snapshot the chain's view for the status API.
598
+ let toChainData = (cs: t): chainData => {
599
+ chainId: cs.chainConfig.id->Int.toFloat,
600
+ poweredByHyperSync: (cs.sourceManager->SourceManager.getActiveSource).poweredByHyperSync,
601
+ firstEventBlockNumber: cs.fetchState.firstEventBlock,
602
+ latestProcessedBlock: cs.committedProgressBlockNumber === -1
603
+ ? None
604
+ : Some(cs.committedProgressBlockNumber),
605
+ timestampCaughtUpToHeadOrEndblock: cs.timestampCaughtUpToHeadOrEndblock,
606
+ numEventsProcessed: cs.numEventsProcessed,
607
+ latestFetchedBlockNumber: Pervasives.max(cs.fetchState->FetchState.bufferBlockNumber, 0),
608
+ knownHeight: cs->hasProcessedToEndblock
609
+ ? cs.fetchState.endBlock->Option.getOr(cs.fetchState.knownHeight)
610
+ : cs.fetchState.knownHeight,
611
+ numBatchesFetched: 0,
612
+ startBlock: cs.fetchState.startBlock,
613
+ endBlock: cs.fetchState.endBlock,
614
+ numAddresses: cs.fetchState->FetchState.numAddresses,
615
+ }
616
+
617
+ // Snapshot the inputs a batch build needs from this chain.
618
+ let toChainBeforeBatch = (cs: t): Batch.chainBeforeBatch => {
619
+ fetchState: cs.fetchState,
620
+ progressBlockNumber: cs.committedProgressBlockNumber,
621
+ totalEventsProcessed: cs.numEventsProcessed,
622
+ sourceBlockNumber: cs.fetchState.knownHeight,
623
+ reorgDetection: cs.reorgDetection,
624
+ chainConfig: cs.chainConfig,
625
+ }
626
+
627
+ // Whether the chain's post-batch fetch frontier is ready to cross into the reorg
628
+ // threshold, using the batch's progressed frontier when this chain advanced.
629
+ let isReadyToEnterReorgThresholdAfterBatch = (cs: t, ~batch: Batch.t) => {
630
+ let fetchState = switch batch.progressedChainsById->Utils.Dict.dangerouslyGetByIntNonOption(
631
+ cs.fetchState.chainId,
632
+ ) {
633
+ | Some(chainAfterBatch) => chainAfterBatch.fetchState
634
+ | None => cs.fetchState
635
+ }
636
+ fetchState->FetchState.isReadyToEnterReorgThreshold
637
+ }
638
+
469
639
  // Commit the post-batch fetch frontier for a chain that progressed in the batch,
470
640
  // applying blockLag when this batch also crosses into the reorg threshold.
471
641
  let advanceAfterBatch = (cs: t, ~batch: Batch.t, ~enteringReorgThreshold) =>
@@ -526,6 +696,8 @@ let applyBatchProgress = (cs: t, ~batch: Batch.t) => {
526
696
 
527
697
  cs.committedProgressBlockNumber = chainAfterBatch.progressBlockNumber
528
698
  cs.numEventsProcessed = chainAfterBatch.totalEventsProcessed
699
+ // Processed blocks' transactions are no longer needed.
700
+ cs.transactionStore->TransactionStore.prune(chainAfterBatch.progressBlockNumber)
529
701
  cs.isProgressAtHead = cs.isProgressAtHead || chainAfterBatch.isProgressAtHeadWhenBatchCreated
530
702
  switch cs.safeCheckpointTracking {
531
703
  | Some(safeCheckpointTracking) =>
@@ -599,6 +771,7 @@ let rollback = (
599
771
  | None => ()
600
772
  }
601
773
  cs.fetchState = cs.fetchState->FetchState.rollback(~targetBlockNumber=newProgressBlockNumber)
774
+ cs.transactionStore->TransactionStore.rollback(newProgressBlockNumber)
602
775
  cs.committedProgressBlockNumber = newProgressBlockNumber
603
776
  cs.numEventsProcessed = newTotalEventsProcessed
604
777
  | None =>
@@ -609,6 +782,7 @@ let rollback = (
609
782
  )
610
783
  cs.fetchState =
611
784
  cs.fetchState->FetchState.rollback(~targetBlockNumber=rollbackTargetBlockNumber)
785
+ cs.transactionStore->TransactionStore.rollback(rollbackTargetBlockNumber)
612
786
  cs.committedProgressBlockNumber = Pervasives.min(
613
787
  cs.committedProgressBlockNumber,
614
788
  rollbackTargetBlockNumber,
@@ -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";
@@ -17,7 +18,9 @@ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
17
18
  import * as ReorgDetection from "./ReorgDetection.res.mjs";
18
19
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
19
20
  import * as HyperFuelSource from "./sources/HyperFuelSource.res.mjs";
21
+ import * as Primitive_float from "@rescript/runtime/lib/es6/Primitive_float.js";
20
22
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
23
+ import * as TransactionStore from "./sources/TransactionStore.res.mjs";
21
24
  import * as SvmHyperSyncSource from "./sources/SvmHyperSyncSource.res.mjs";
22
25
  import * as SafeCheckpointTracking from "./SafeCheckpointTracking.res.mjs";
23
26
 
@@ -35,11 +38,12 @@ function configAddresses(chainConfig) {
35
38
  return addresses;
36
39
  }
37
40
 
38
- 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) {
39
42
  let safeCheckpointTracking = safeCheckpointTrackingOpt !== undefined ? Primitive_option.valFromOption(safeCheckpointTrackingOpt) : undefined;
40
43
  let numEventsProcessed = numEventsProcessedOpt !== undefined ? numEventsProcessedOpt : 0;
41
44
  let timestampCaughtUpToHeadOrEndblock = timestampCaughtUpToHeadOrEndblockOpt !== undefined ? Primitive_option.valFromOption(timestampCaughtUpToHeadOrEndblockOpt) : undefined;
42
45
  let isProgressAtHead = isProgressAtHeadOpt !== undefined ? isProgressAtHeadOpt : false;
46
+ let transactionFieldMask = transactionFieldMaskOpt !== undefined ? transactionFieldMaskOpt : 0;
43
47
  return {
44
48
  logger: logger,
45
49
  fetchState: fetchState,
@@ -49,8 +53,11 @@ function make(chainConfig, fetchState, sourceManager, reorgDetection, committedP
49
53
  timestampCaughtUpToHeadOrEndblock: timestampCaughtUpToHeadOrEndblock,
50
54
  committedProgressBlockNumber: committedProgressBlockNumber,
51
55
  numEventsProcessed: numEventsProcessed,
56
+ pendingBudget: 0,
52
57
  reorgDetection: reorgDetection,
53
- safeCheckpointTracking: safeCheckpointTracking
58
+ safeCheckpointTracking: safeCheckpointTracking,
59
+ transactionStore: TransactionStore.make(),
60
+ transactionFieldMask: transactionFieldMask
54
61
  };
55
62
  }
56
63
 
@@ -169,7 +176,7 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
169
176
  sources$1 = sources._0;
170
177
  break;
171
178
  }
172
- 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);
173
180
  }
174
181
 
175
182
  function makeFromConfig(chainConfig, config, registrations, knownHeight) {
@@ -193,10 +200,6 @@ function logger(cs) {
193
200
  return cs.logger;
194
201
  }
195
202
 
196
- function fetchState(cs) {
197
- return cs.fetchState;
198
- }
199
-
200
203
  function sourceManager(cs) {
201
204
  return cs.sourceManager;
202
205
  }
@@ -225,10 +228,63 @@ function numEventsProcessed(cs) {
225
228
  return cs.numEventsProcessed;
226
229
  }
227
230
 
231
+ function pendingBudget(cs) {
232
+ return cs.pendingBudget;
233
+ }
234
+
228
235
  function timestampCaughtUpToHeadOrEndblock(cs) {
229
236
  return cs.timestampCaughtUpToHeadOrEndblock;
230
237
  }
231
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
+
275
+ function startFetchingQueries(cs, queries) {
276
+ FetchState.startFetchingQueries(cs.fetchState, queries);
277
+ cs.pendingBudget = cs.pendingBudget + Stdlib_Array.reduce(queries, 0, (acc, query) => acc + query.estResponseSize);
278
+ }
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
+
232
288
  function hasProcessedToEndblock(cs) {
233
289
  let fetchState = cs.fetchState;
234
290
  let committedProgressBlockNumber = cs.committedProgressBlockNumber;
@@ -261,9 +317,33 @@ function isFetchingAtHead(cs) {
261
317
  return FetchState.isFetchingAtHead(cs.fetchState);
262
318
  }
263
319
 
264
- 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
+ }
265
344
  let fs = newItemsWithDcs.length !== 0 ? FetchState.registerDynamicContracts(cs.fetchState, newItemsWithDcs) : cs.fetchState;
266
345
  cs.fetchState = FetchState.updateKnownHeight(FetchState.handleQueryResult(fs, query, latestFetchedBlock, newItems), knownHeight);
346
+ cs.pendingBudget = Primitive_float.max(0, cs.pendingBudget - query.estResponseSize);
267
347
  }
268
348
 
269
349
  function registerReorgGuard(cs, blockHashes, knownHeight) {
@@ -277,6 +357,7 @@ function prepareReorg(cs, eventsProcessedDiff) {
277
357
  cs.numEventsProcessed = cs.numEventsProcessed + eventsProcessedDiff;
278
358
  }
279
359
  cs.fetchState = FetchState.resetPendingQueries(cs.fetchState);
360
+ cs.pendingBudget = 0;
280
361
  }
281
362
 
282
363
  function updateKnownHeight(cs, knownHeight) {
@@ -331,6 +412,48 @@ function enterReorgThreshold(cs) {
331
412
  cs.fetchState = FetchState.updateInternal(cs.fetchState, undefined, undefined, undefined, cs.chainConfig.blockLag, undefined);
332
413
  }
333
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
+
334
457
  function advanceAfterBatch(cs, batch, enteringReorgThreshold) {
335
458
  let chainAfterBatch = batch.progressedChainsById[cs.fetchState.chainId];
336
459
  if (chainAfterBatch !== undefined) {
@@ -381,6 +504,7 @@ function applyBatchProgress(cs, batch) {
381
504
  }
382
505
  cs.committedProgressBlockNumber = chainAfterBatch.progressBlockNumber;
383
506
  cs.numEventsProcessed = chainAfterBatch.totalEventsProcessed;
507
+ cs.transactionStore.prune(chainAfterBatch.progressBlockNumber);
384
508
  cs.isProgressAtHead = cs.isProgressAtHead || chainAfterBatch.isProgressAtHeadWhenBatchCreated;
385
509
  let safeCheckpointTracking = cs.safeCheckpointTracking;
386
510
  if (safeCheckpointTracking !== undefined) {
@@ -402,6 +526,7 @@ function rollback(cs, newProgressBlockNumber, eventsProcessedDiff, rollbackTarge
402
526
  if (isReorgChain) {
403
527
  cs.reorgDetection = ReorgDetection.rollbackToValidBlockNumber(cs.reorgDetection, rollbackTargetBlockNumber);
404
528
  cs.fetchState = FetchState.rollback(cs.fetchState, rollbackTargetBlockNumber);
529
+ cs.transactionStore.rollback(rollbackTargetBlockNumber);
405
530
  cs.committedProgressBlockNumber = Primitive_int.min(cs.committedProgressBlockNumber, rollbackTargetBlockNumber);
406
531
  return;
407
532
  } else {
@@ -423,6 +548,7 @@ function rollback(cs, newProgressBlockNumber, eventsProcessedDiff, rollbackTarge
423
548
  cs.safeCheckpointTracking = SafeCheckpointTracking.rollback(safeCheckpointTracking, newProgressBlockNumber);
424
549
  }
425
550
  cs.fetchState = FetchState.rollback(cs.fetchState, newProgressBlockNumber);
551
+ cs.transactionStore.rollback(newProgressBlockNumber);
426
552
  cs.committedProgressBlockNumber = newProgressBlockNumber;
427
553
  cs.numEventsProcessed = newTotalEventsProcessed;
428
554
  }
@@ -433,7 +559,6 @@ export {
433
559
  makeFromConfig,
434
560
  makeFromDbState,
435
561
  logger,
436
- fetchState,
437
562
  sourceManager,
438
563
  chainConfig,
439
564
  reorgDetection,
@@ -441,13 +566,32 @@ export {
441
566
  isProgressAtHead,
442
567
  committedProgressBlockNumber,
443
568
  numEventsProcessed,
569
+ pendingBudget,
570
+ startFetchingQueries,
444
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,
445
586
  hasProcessedToEndblock,
446
587
  getHighestBlockBelowThreshold,
447
588
  isActivelyIndexing,
448
589
  isReady,
449
590
  isFetchingAtHead,
591
+ isAtHeadWithoutEndBlock,
450
592
  handleQueryResult,
593
+ materializeBatchItems,
594
+ materializePageItems,
451
595
  registerReorgGuard,
452
596
  prepareReorg,
453
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
@@ -46,14 +46,59 @@ let safeCheckpointTracking: t => option<SafeCheckpointTracking.t>
46
46
  let isProgressAtHead: t => bool
47
47
  let committedProgressBlockNumber: t => int
48
48
  let numEventsProcessed: t => float
49
+ let pendingBudget: t => float
50
+ let startFetchingQueries: (t, ~queries: array<FetchState.query>) => unit
49
51
  let timestampCaughtUpToHeadOrEndblock: t => option<Date.t>
50
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
+
51
95
  // Derived (pure).
52
96
  let hasProcessedToEndblock: t => bool
53
97
  let getHighestBlockBelowThreshold: t => int
54
98
  let isActivelyIndexing: t => bool
55
99
  let isReady: t => bool
56
100
  let isFetchingAtHead: t => bool
101
+ let isAtHeadWithoutEndBlock: t => bool
57
102
 
58
103
  // State transitions. The chain state is mutated only through these.
59
104
  let handleQueryResult: (
@@ -63,7 +108,14 @@ let handleQueryResult: (
63
108
  ~newItemsWithDcs: array<Internal.item>,
64
109
  ~latestFetchedBlock: FetchState.blockNumberAndTimestamp,
65
110
  ~knownHeight: int,
111
+ ~transactionStore: option<TransactionStore.t>,
66
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>
67
119
  let registerReorgGuard: (
68
120
  t,
69
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,