envio 3.6.1 → 3.7.0

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 (80) hide show
  1. package/index.d.ts +197 -9
  2. package/package.json +6 -6
  3. package/src/Batch.res +64 -25
  4. package/src/Batch.res.mjs +69 -46
  5. package/src/ChainFetching.res +13 -17
  6. package/src/ChainFetching.res.mjs +9 -14
  7. package/src/ChainState.res +160 -119
  8. package/src/ChainState.res.mjs +75 -46
  9. package/src/ChainState.resi +15 -12
  10. package/src/Config.res +9 -2
  11. package/src/Config.res.mjs +4 -4
  12. package/src/Core.res +5 -0
  13. package/src/Ecosystem.res +0 -1
  14. package/src/Envio.res +16 -3
  15. package/src/EventConfigBuilder.res +155 -40
  16. package/src/EventConfigBuilder.res.mjs +76 -24
  17. package/src/EventProcessing.res +4 -8
  18. package/src/EventProcessing.res.mjs +4 -4
  19. package/src/EventUtils.res +3 -47
  20. package/src/FetchState.res +124 -104
  21. package/src/FetchState.res.mjs +116 -100
  22. package/src/HandlerRegister.res +108 -2
  23. package/src/HandlerRegister.res.mjs +59 -10
  24. package/src/HandlerRegister.resi +4 -0
  25. package/src/Internal.res +84 -24
  26. package/src/Internal.res.mjs +33 -2
  27. package/src/LazyLoader.res +12 -11
  28. package/src/LazyLoader.res.mjs +13 -7
  29. package/src/Main.res +22 -9
  30. package/src/Main.res.mjs +18 -5
  31. package/src/MemoryStorage.res +724 -0
  32. package/src/MemoryStorage.res.mjs +601 -0
  33. package/src/PgStorage.res +18 -7
  34. package/src/PgStorage.res.mjs +14 -11
  35. package/src/ReorgDetection.res +0 -222
  36. package/src/ReorgDetection.res.mjs +0 -163
  37. package/src/Rollback.res +14 -15
  38. package/src/Rollback.res.mjs +4 -5
  39. package/src/SimulateItems.res +2 -2
  40. package/src/Utils.res +3 -0
  41. package/src/bindings/ClickHouse.res +16 -11
  42. package/src/bindings/ClickHouse.res.mjs +10 -10
  43. package/src/bindings/Vitest.res +1 -1
  44. package/src/sources/BlockStore.res +115 -5
  45. package/src/sources/BlockStore.res.mjs +25 -0
  46. package/src/sources/Evm.res +0 -1
  47. package/src/sources/Evm.res.mjs +0 -1
  48. package/src/sources/EvmHyperSyncSource.res +19 -84
  49. package/src/sources/EvmHyperSyncSource.res.mjs +24 -60
  50. package/src/sources/Fuel.res +24 -4
  51. package/src/sources/Fuel.res.mjs +23 -3
  52. package/src/sources/FuelHyperSync.res +8 -3
  53. package/src/sources/FuelHyperSync.res.mjs +5 -4
  54. package/src/sources/FuelHyperSync.resi +3 -1
  55. package/src/sources/FuelHyperSyncClient.res +7 -10
  56. package/src/sources/FuelHyperSyncSource.res +18 -45
  57. package/src/sources/FuelHyperSyncSource.res.mjs +18 -35
  58. package/src/sources/HyperSync.res +49 -229
  59. package/src/sources/HyperSync.res.mjs +74 -191
  60. package/src/sources/HyperSync.resi +11 -35
  61. package/src/sources/HyperSyncClient.res +9 -79
  62. package/src/sources/HyperSyncClient.res.mjs +2 -6
  63. package/src/sources/RequestStat.res +5 -0
  64. package/src/sources/RequestStat.res.mjs +2 -0
  65. package/src/sources/RpcSource.res +149 -43
  66. package/src/sources/RpcSource.res.mjs +104 -41
  67. package/src/sources/SimulateSource.res +8 -5
  68. package/src/sources/SimulateSource.res.mjs +6 -6
  69. package/src/sources/Source.res +89 -16
  70. package/src/sources/Source.res.mjs +50 -1
  71. package/src/sources/SourceManager.res +255 -50
  72. package/src/sources/SourceManager.res.mjs +149 -55
  73. package/src/sources/SourceManager.resi +2 -6
  74. package/src/sources/Svm.res +0 -7
  75. package/src/sources/Svm.res.mjs +0 -8
  76. package/src/sources/SvmHyperSyncClient.res +21 -16
  77. package/src/sources/SvmHyperSyncClient.res.mjs +3 -4
  78. package/src/sources/SvmHyperSyncSource.res +25 -117
  79. package/src/sources/SvmHyperSyncSource.res.mjs +8 -121
  80. package/svm.schema.json +3 -2
@@ -18,7 +18,6 @@ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
18
18
  import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
19
19
  import * as SourceManager from "./sources/SourceManager.res.mjs";
20
20
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
21
- import * as ReorgDetection from "./ReorgDetection.res.mjs";
22
21
  import * as SimulateSource from "./sources/SimulateSource.res.mjs";
23
22
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
24
23
  import * as Primitive_float from "@rescript/runtime/lib/es6/Primitive_float.js";
@@ -50,7 +49,7 @@ function validateOnEventRegistrations(chainId, registrations) {
50
49
  });
51
50
  }
52
51
 
53
- function make(chainConfig, fetchState, onEventRegistrationsOpt, addressStore, sourceManager, reorgDetection, committedProgressBlockNumber, safeCheckpointTrackingOpt, numEventsProcessedOpt, timestampCaughtUpToHeadOrEndblockOpt, isProgressAtHeadOpt, transactionStoreOpt, chainDensityOpt, blockStoreOpt, reorgThresholdReadyToleranceOpt, perChainEntitiesOpt, logger) {
52
+ function make(chainConfig, fetchState, onEventRegistrationsOpt, addressStore, sourceManager, committedProgressBlockNumber, safeCheckpointTrackingOpt, shouldRollbackOnReorg, maxReorgDepth, numEventsProcessedOpt, timestampCaughtUpToHeadOrEndblockOpt, isProgressAtHeadOpt, transactionStoreOpt, chainDensityOpt, blockStoreOpt, reorgThresholdReadyToleranceOpt, perChainEntitiesOpt, logger) {
54
53
  let onEventRegistrations = onEventRegistrationsOpt !== undefined ? onEventRegistrationsOpt : [];
55
54
  let safeCheckpointTracking = safeCheckpointTrackingOpt !== undefined ? Primitive_option.valFromOption(safeCheckpointTrackingOpt) : undefined;
56
55
  let numEventsProcessed = numEventsProcessedOpt !== undefined ? numEventsProcessedOpt : 0;
@@ -77,8 +76,9 @@ function make(chainConfig, fetchState, onEventRegistrationsOpt, addressStore, so
77
76
  pendingBudget: 0,
78
77
  chainDensity: chainDensity,
79
78
  entities: EntityTables.make(perChainEntities),
80
- reorgDetection: reorgDetection,
81
79
  safeCheckpointTracking: safeCheckpointTracking,
80
+ shouldRollbackOnReorg: shouldRollbackOnReorg,
81
+ maxReorgDepth: maxReorgDepth,
82
82
  transactionStore: transactionStore,
83
83
  blockStore: blockStore,
84
84
  reorgThresholdReadyTolerance: reorgThresholdReadyTolerance,
@@ -132,7 +132,7 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
132
132
  tmp = undefined;
133
133
  break;
134
134
  }
135
- let fetchState = FetchState.make(startBlock, endBlock, onEventRegistrations, addressStore, indexingAddresses, config.maxAddrInPartition, chainConfig.id, (config.batchSize << 1), knownHeight, progressBlockNumber, match.onBlockRegistrations, Primitive_int.max(!config.shouldRollbackOnReorg || isInReorgThreshold ? 0 : chainConfig.maxReorgDepth, chainConfig.blockLag), Primitive_option.some(firstEventBlock), Primitive_option.some(tmp), isResumed);
135
+ let fetchState = FetchState.make(startBlock, endBlock, onEventRegistrations, addressStore, indexingAddresses, config.maxAddrInPartition, chainConfig.id, (config.batchSize << 1), knownHeight, progressBlockNumber, match.onBlockRegistrations, Primitive_int.max(!config.shouldRollbackOnReorg || isInReorgThreshold ? 0 : maxReorgDepth, chainConfig.blockLag), Primitive_option.some(firstEventBlock), Primitive_option.some(tmp), isResumed);
136
136
  let chainReorgCheckpoints = Stdlib_Array.filterMap(reorgCheckpoints, reorgCheckpoint => {
137
137
  if (reorgCheckpoint.chain_id === chainConfig.id) {
138
138
  return reorgCheckpoint;
@@ -181,15 +181,23 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
181
181
  );
182
182
  break;
183
183
  case "SimulateSourceConfig" :
184
- sources$1 = [SimulateSource.make(sources.items, sources.endBlock, chainId, addressStore)];
184
+ sources$1 = [SimulateSource.make(sources.items, sources.endBlock, chainId, addressStore, config.ecosystem.name)];
185
185
  break;
186
186
  case "CustomSources" :
187
187
  sources$1 = sources._0;
188
188
  break;
189
189
  }
190
+ let blockStore = BlockStore.make(config.ecosystem.name, !lowercaseAddresses);
191
+ if (Utils.$$Array.notEmpty(chainReorgCheckpoints)) {
192
+ let seedPage = BlockStore.fromJs(chainReorgCheckpoints.map(cp => ({
193
+ blockNumber: cp.block_number,
194
+ blockHash: cp.block_hash
195
+ })), config.ecosystem.name, !lowercaseAddresses);
196
+ blockStore.merge(seedPage, 0, false);
197
+ }
190
198
  let firstEventBlock$1 = fetchState.firstEventBlock;
191
199
  let chainDensity = firstEventBlock$1 !== undefined && progressBlockNumber > firstEventBlock$1 && numEventsProcessed > 0 ? numEventsProcessed / (progressBlockNumber - firstEventBlock$1 | 0) : undefined;
192
- return make(chainConfig, fetchState, onEventRegistrations, addressStore, 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, Primitive_option.some(TransactionStore.make(config.ecosystem.name, !lowercaseAddresses)), Primitive_option.some(chainDensity), Primitive_option.some(BlockStore.make(config.ecosystem.name, !lowercaseAddresses)), config.reorgThresholdReadyTolerance, EntityTables.perChain(config.allEntities), logger);
200
+ return make(chainConfig, fetchState, onEventRegistrations, addressStore, SourceManager.make(sources$1, isRealtime, undefined, undefined, undefined, reducedPollingInterval, undefined, undefined), progressBlockNumber, Primitive_option.some(SafeCheckpointTracking.make(maxReorgDepth, config.shouldRollbackOnReorg, chainReorgCheckpoints)), config.shouldRollbackOnReorg, maxReorgDepth, numEventsProcessed, Primitive_option.some(timestampCaughtUpToHeadOrEndblock), undefined, Primitive_option.some(TransactionStore.make(config.ecosystem.name, !lowercaseAddresses)), Primitive_option.some(chainDensity), Primitive_option.some(blockStore), config.reorgThresholdReadyTolerance, EntityTables.perChain(config.allEntities), logger);
193
201
  }
194
202
 
195
203
  function makeFromConfig(chainConfig, config, registrationsByChainId, knownHeight) {
@@ -212,6 +220,10 @@ function logger(cs) {
212
220
  return cs.logger;
213
221
  }
214
222
 
223
+ function blockStore(cs) {
224
+ return cs.blockStore;
225
+ }
226
+
215
227
  function entities(cs) {
216
228
  return cs.entities;
217
229
  }
@@ -228,8 +240,16 @@ function chainConfig(cs) {
228
240
  return cs.chainConfig;
229
241
  }
230
242
 
231
- function reorgDetection(cs) {
232
- return cs.reorgDetection;
243
+ function shouldRollbackOnReorg(cs) {
244
+ return cs.shouldRollbackOnReorg;
245
+ }
246
+
247
+ function getReorgThresholdBlockNumbersBelow(cs, blockNumber) {
248
+ return cs.blockStore.getHashedBlockNumbers(Primitive_int.max(cs.fetchState.knownHeight - cs.maxReorgDepth | 0, 0), blockNumber);
249
+ }
250
+
251
+ function getLatestValidScannedBlock(cs, blockStore, blockNumbers) {
252
+ return Primitive_option.fromNull(cs.blockStore.latestValidBlockFromStore(blockStore, blockNumbers));
233
253
  }
234
254
 
235
255
  function safeCheckpointTracking(cs) {
@@ -447,7 +467,7 @@ function isDurablyCaughtUp(cs) {
447
467
  }
448
468
 
449
469
  function getHighestBlockBelowThreshold(cs) {
450
- let highestBlockBelowThreshold = cs.fetchState.knownHeight - cs.chainConfig.maxReorgDepth | 0;
470
+ let highestBlockBelowThreshold = cs.fetchState.knownHeight - cs.maxReorgDepth | 0;
451
471
  if (highestBlockBelowThreshold < 0) {
452
472
  return 0;
453
473
  } else {
@@ -471,7 +491,7 @@ function isAtHeadWithoutEndBlock(cs) {
471
491
  }
472
492
  }
473
493
 
474
- function groupBatchItems(items, includeBlocks) {
494
+ function groupBatchItems(items) {
475
495
  let txBlockNumbers = [];
476
496
  let transactionIndices = [];
477
497
  let transactionMasks = [];
@@ -490,7 +510,7 @@ function groupBatchItems(items, includeBlocks) {
490
510
  let match = item.payload.transaction;
491
511
  if (match == null) {
492
512
  let transactionIndex = item.transactionIndex;
493
- let mask = item.onEventRegistration.eventConfig.transactionFieldMask;
513
+ let mask = item.onEventRegistration.fieldSelection.transactionMask;
494
514
  if (mask !== 0) {
495
515
  anyTransactionFieldSelected.contents = true;
496
516
  }
@@ -505,14 +525,11 @@ function groupBatchItems(items, includeBlocks) {
505
525
  payloadGroups.push([item.payload]);
506
526
  }
507
527
  }
508
- if (!includeBlocks) {
509
- return;
510
- }
511
528
  let match$1 = item.payload.block;
512
529
  if (!(match$1 == null)) {
513
530
  return;
514
531
  }
515
- let mask$1 = item.onEventRegistration.eventConfig.blockFieldMask;
532
+ let mask$1 = item.onEventRegistration.fieldSelection.blockMask;
516
533
  let last$1 = blockItemGroups.length - 1 | 0;
517
534
  if (last$1 >= 0 && blockBlockNumbers[last$1] === blockNumber) {
518
535
  blockItemGroups[last$1].push(item);
@@ -573,48 +590,50 @@ async function applyBlockGroups(store, g) {
573
590
  });
574
591
  }
575
592
 
576
- function includeBlocksForEcosystem(ecosystem) {
577
- switch (ecosystem) {
578
- case "fuel" :
579
- return false;
580
- case "evm" :
581
- case "svm" :
582
- return true;
583
- }
584
- }
585
-
586
- async function materializeBatchItems(cs, items, ecosystem) {
587
- let match = groupBatchItems(items, includeBlocksForEcosystem(ecosystem));
593
+ async function materializeBatchItems(cs, items) {
594
+ let match = groupBatchItems(items);
588
595
  await Promise.all([
589
596
  applyTransactionGroups(cs.transactionStore, match[0]),
590
597
  applyBlockGroups(cs.blockStore, match[1])
591
598
  ]);
592
599
  }
593
600
 
594
- async function materializePageItems(items, transactionStore, blockStore, ecosystem) {
595
- let match = groupBatchItems(items, includeBlocksForEcosystem(ecosystem));
601
+ async function materializePageItems(items, transactionStore, blockStore) {
602
+ let match = groupBatchItems(items);
596
603
  await Promise.all([
597
604
  transactionStore !== undefined ? applyTransactionGroups(Primitive_option.valFromOption(transactionStore), match[0]) : Promise.resolve(),
598
- blockStore !== undefined ? applyBlockGroups(Primitive_option.valFromOption(blockStore), match[1]) : Promise.resolve()
605
+ applyBlockGroups(blockStore, match[1])
599
606
  ]);
600
607
  }
601
608
 
602
- function handleQueryResult(cs, query, newItems, newRegistrations, latestFetchedBlock, knownHeight, txPage, blockPage) {
609
+ function handleQueryResult(cs, query, newItems, newRegistrations, latestFetchedBlock, knownHeight, txPage) {
603
610
  if (txPage !== undefined) {
604
611
  cs.transactionStore.merge(Primitive_option.valFromOption(txPage));
605
612
  }
606
- if (blockPage !== undefined) {
607
- cs.blockStore.merge(Primitive_option.valFromOption(blockPage));
608
- }
609
- let fs = newRegistrations.length !== 0 ? FetchState.registerDynamicContracts(cs.fetchState, cs.addressStore, Primitive_int.max(knownHeight, latestFetchedBlock.blockNumber), newRegistrations) : cs.fetchState;
613
+ let fs = newRegistrations.length !== 0 ? FetchState.registerDynamicContracts(cs.fetchState, cs.addressStore, Primitive_int.max(knownHeight, latestFetchedBlock), newRegistrations) : cs.fetchState;
610
614
  cs.fetchState = FetchState.updateKnownHeight(FetchState.handleQueryResult(fs, query, latestFetchedBlock, newItems), knownHeight);
611
615
  cs.pendingBudget = Primitive_float.max(0, cs.pendingBudget - query.itemsEst);
612
616
  }
613
617
 
614
- function registerReorgGuard(cs, blockHashes, knownHeight) {
615
- let match = ReorgDetection.registerReorgGuard(cs.reorgDetection, blockHashes, knownHeight);
616
- cs.reorgDetection = match[0];
617
- return match[1];
618
+ function registerReorgGuard(cs, blockStore, knownHeight) {
619
+ let match = cs.blockStore.merge(blockStore, Primitive_int.max(knownHeight - cs.maxReorgDepth | 0, 0), !cs.shouldRollbackOnReorg);
620
+ if (match === null) {
621
+ return "NoReorg";
622
+ }
623
+ let blockNumber = match.blockNumber;
624
+ return {
625
+ TAG: "ReorgDetected",
626
+ _0: {
627
+ scannedBlock: {
628
+ blockHash: match.storedHash,
629
+ blockNumber: blockNumber
630
+ },
631
+ receivedBlock: {
632
+ blockHash: match.receivedHash,
633
+ blockNumber: blockNumber
634
+ }
635
+ }
636
+ };
618
637
  }
619
638
 
620
639
  function prepareReorg(cs, eventsProcessedDiff) {
@@ -720,9 +739,20 @@ function toMetrics(cs) {
720
739
  }
721
740
 
722
741
  function toChainBeforeBatch(cs) {
742
+ let match = cs.blockStore.getHashes(Primitive_int.max(cs.fetchState.knownHeight - cs.maxReorgDepth | 0, 0), cs.fetchState.knownHeight + 1 | 0);
743
+ let hashes = match.hashes;
744
+ let blockNumbers = match.blockNumbers;
745
+ let hashByBlockNumber = {};
746
+ blockNumbers.forEach((blockNumber, idx) => {
747
+ hashByBlockNumber[blockNumber] = hashes[idx];
748
+ });
723
749
  return {
724
750
  fetchState: cs.fetchState,
725
- reorgDetection: cs.reorgDetection,
751
+ scannedHashes: {
752
+ blockNumbers: blockNumbers,
753
+ hashByBlockNumber: hashByBlockNumber
754
+ },
755
+ shouldRollbackOnReorg: cs.shouldRollbackOnReorg,
726
756
  progressBlockNumber: cs.committedProgressBlockNumber,
727
757
  sourceBlockNumber: cs.fetchState.knownHeight,
728
758
  totalEventsProcessed: cs.numEventsProcessed,
@@ -804,7 +834,7 @@ function applyBatchProgress(cs, batch, blockTimestampName) {
804
834
  cs.processingBlockNumber = Primitive_int.max(cs.processingBlockNumber, chainAfterBatch.progressBlockNumber);
805
835
  cs.numEventsProcessed = chainAfterBatch.totalEventsProcessed;
806
836
  cs.transactionStore.prune(chainAfterBatch.progressBlockNumber);
807
- cs.blockStore.prune(chainAfterBatch.progressBlockNumber);
837
+ cs.blockStore.prune(chainAfterBatch.progressBlockNumber, cs.fetchState.knownHeight - cs.maxReorgDepth | 0);
808
838
  cs.isProgressAtHead = cs.isProgressAtHead || chainAfterBatch.isProgressAtHeadWhenBatchCreated;
809
839
  let safeCheckpointTracking = cs.safeCheckpointTracking;
810
840
  if (safeCheckpointTracking !== undefined) {
@@ -823,7 +853,6 @@ function markReady(cs, readyAt) {
823
853
  function rollback(cs, newProgressBlockNumber, eventsProcessedDiff, rollbackTargetBlockNumber, isReorgChain) {
824
854
  if (newProgressBlockNumber === undefined) {
825
855
  if (isReorgChain) {
826
- cs.reorgDetection = ReorgDetection.rollbackToValidBlockNumber(cs.reorgDetection, rollbackTargetBlockNumber);
827
856
  cs.fetchState = FetchState.rollback(cs.fetchState, cs.addressStore, rollbackTargetBlockNumber);
828
857
  cs.transactionStore.rollback(rollbackTargetBlockNumber);
829
858
  cs.blockStore.rollback(rollbackTargetBlockNumber);
@@ -835,9 +864,6 @@ function rollback(cs, newProgressBlockNumber, eventsProcessedDiff, rollbackTarge
835
864
  }
836
865
  }
837
866
  let newTotalEventsProcessed = cs.numEventsProcessed - Stdlib_Option.getOrThrow(eventsProcessedDiff, "Missing events-processed diff for rolled-back chain");
838
- if (isReorgChain) {
839
- cs.reorgDetection = ReorgDetection.rollbackToValidBlockNumber(cs.reorgDetection, rollbackTargetBlockNumber);
840
- }
841
867
  let safeCheckpointTracking = cs.safeCheckpointTracking;
842
868
  if (safeCheckpointTracking !== undefined) {
843
869
  cs.safeCheckpointTracking = SafeCheckpointTracking.rollback(safeCheckpointTracking, newProgressBlockNumber);
@@ -856,11 +882,14 @@ export {
856
882
  makeFromConfig,
857
883
  makeFromDbState,
858
884
  logger,
885
+ blockStore,
859
886
  entities,
860
887
  resetEntities,
861
888
  sourceManager,
862
889
  chainConfig,
863
- reorgDetection,
890
+ shouldRollbackOnReorg,
891
+ getReorgThresholdBlockNumbersBelow,
892
+ getLatestValidScannedBlock,
864
893
  safeCheckpointTracking,
865
894
  isProgressAtHead,
866
895
  committedProgressBlockNumber,
@@ -11,9 +11,10 @@ let make: (
11
11
  ~onEventRegistrations: array<Internal.onEventRegistration>=?,
12
12
  ~addressStore: AddressStore.t,
13
13
  ~sourceManager: SourceManager.t,
14
- ~reorgDetection: ReorgDetection.t,
15
14
  ~committedProgressBlockNumber: int,
16
15
  ~safeCheckpointTracking: option<SafeCheckpointTracking.t>=?,
16
+ ~shouldRollbackOnReorg: bool,
17
+ ~maxReorgDepth: int,
17
18
  ~numEventsProcessed: float=?,
18
19
  ~timestampCaughtUpToHeadOrEndblock: option<Date.t>=?,
19
20
  ~isProgressAtHead: bool=?,
@@ -45,11 +46,19 @@ let makeFromDbState: (
45
46
 
46
47
  // Accessors.
47
48
  let logger: t => Pino.t
49
+ let blockStore: t => BlockStore.t
48
50
  let entities: t => EntityTables.t
49
51
  let resetEntities: (t, ~perChainEntities: array<Internal.entityConfig>) => unit
50
52
  let sourceManager: t => SourceManager.t
51
53
  let chainConfig: t => Config.chain
52
- let reorgDetection: t => ReorgDetection.t
54
+ let shouldRollbackOnReorg: t => bool
55
+ // Reorg-scan reads over the chain's block store, for the rollback flow.
56
+ let getReorgThresholdBlockNumbersBelow: (t, ~blockNumber: int) => array<int>
57
+ let getLatestValidScannedBlock: (
58
+ t,
59
+ ~blockStore: BlockStore.t,
60
+ ~blockNumbers: array<int>,
61
+ ) => option<int>
53
62
  let safeCheckpointTracking: t => option<SafeCheckpointTracking.t>
54
63
  let isProgressAtHead: t => bool
55
64
  let committedProgressBlockNumber: t => int
@@ -120,25 +129,19 @@ let handleQueryResult: (
120
129
  ~query: FetchState.query,
121
130
  ~newItems: array<Internal.item>,
122
131
  ~newRegistrations: array<AddressStore.registration>,
123
- ~latestFetchedBlock: FetchState.blockNumberAndTimestamp,
132
+ ~latestFetchedBlock: int,
124
133
  ~knownHeight: int,
125
134
  ~transactionStore: option<TransactionStore.t>,
126
- ~blockStore: option<BlockStore.t>,
127
135
  ) => unit
128
- let materializeBatchItems: (
129
- t,
130
- ~items: array<Internal.item>,
131
- ~ecosystem: Ecosystem.name,
132
- ) => promise<unit>
136
+ let materializeBatchItems: (t, ~items: array<Internal.item>) => promise<unit>
133
137
  let materializePageItems: (
134
138
  ~items: array<Internal.item>,
135
139
  ~transactionStore: option<TransactionStore.t>,
136
- ~blockStore: option<BlockStore.t>,
137
- ~ecosystem: Ecosystem.name,
140
+ ~blockStore: BlockStore.t,
138
141
  ) => promise<unit>
139
142
  let registerReorgGuard: (
140
143
  t,
141
- ~blockHashes: array<ReorgDetection.blockData>,
144
+ ~blockStore: BlockStore.t,
142
145
  ~knownHeight: int,
143
146
  ) => ReorgDetection.reorgResult
144
147
  let prepareReorg: (t, ~eventsProcessedDiff: option<float>) => unit
package/src/Config.res CHANGED
@@ -1016,8 +1016,15 @@ let fromPublic = (publicConfigJson: JSON.t) => {
1016
1016
  endBlock: ?publicChainConfig["endBlock"],
1017
1017
  maxReorgDepth: switch ecosystemName {
1018
1018
  | Ecosystem.Evm => publicChainConfig["maxReorgDepth"]->Option.getOr(200)
1019
-
1020
- | Ecosystem.Fuel | Ecosystem.Svm => 0
1019
+ // Tower BFT roots a block once 32 votes lock it in (MAX_LOCKOUT_HISTORY
1020
+ // is 31, plus the slot itself), which is what `finalized` waits for — so
1021
+ // 32 is the protocol's own bound on how far a fork can be replaced. That
1022
+ // bound counts *blocks* while the threshold here is measured in slot
1023
+ // numbers, and skipped slots make the slot distance the larger of the
1024
+ // two, so the default keeps a wide margin over it. Slots are ~0.4s, so
1025
+ // even this spans well under two minutes of chain.
1026
+ | Ecosystem.Svm => publicChainConfig["maxReorgDepth"]->Option.getOr(200)
1027
+ | Ecosystem.Fuel => 0
1021
1028
  },
1022
1029
  blockLag: publicChainConfig["blockLag"]->Option.getOr(0),
1023
1030
  contracts,
@@ -685,13 +685,13 @@ function fromPublic(publicConfigJson) {
685
685
  }
686
686
  let tmp;
687
687
  switch (ecosystemName) {
688
- case "evm" :
689
- tmp = Stdlib_Option.getOr(publicChainConfig.maxReorgDepth, 200);
690
- break;
691
688
  case "fuel" :
692
- case "svm" :
693
689
  tmp = 0;
694
690
  break;
691
+ case "evm" :
692
+ case "svm" :
693
+ tmp = Stdlib_Option.getOr(publicChainConfig.maxReorgDepth, 200);
694
+ break;
695
695
  }
696
696
  return {
697
697
  name: chainName,
package/src/Core.res CHANGED
@@ -11,6 +11,8 @@ type fuelHyperSyncClientCtor
11
11
  type transactionStoreCtor
12
12
  type blockStoreCtor
13
13
  type addressStoreCtor
14
+ // Test-only: a local HyperSync server, bound by MockHyperSyncServer in envio-tests.
15
+ type mockHyperSyncServerCtor
14
16
  type fromUserApiOptions = {
15
17
  schema?: string,
16
18
  env?: dict<string>,
@@ -43,6 +45,8 @@ type addon = {
43
45
  blockStore: blockStoreCtor,
44
46
  @as("AddressStore")
45
47
  addressStore: addressStoreCtor,
48
+ @as("MockHyperSyncServer")
49
+ mockHyperSyncServer: mockHyperSyncServerCtor,
46
50
  // Ordered transaction-field names exposed for the field-code contract test
47
51
  // (the ReScript `transactionFields` arrays must match the Rust ordinals).
48
52
  evmTransactionFieldNames: unit => array<string>,
@@ -51,6 +55,7 @@ type addon = {
51
55
  // must match the Rust ordinals).
52
56
  evmBlockFieldNames: unit => array<string>,
53
57
  svmBlockFieldNames: unit => array<string>,
58
+ fuelBlockFieldNames: unit => array<string>,
54
59
  }
55
60
 
56
61
  @module("node:module") external createRequire: string => {..} = "createRequire"
package/src/Ecosystem.res CHANGED
@@ -5,7 +5,6 @@ type t = {
5
5
  blockNumberName: string,
6
6
  blockTimestampName: string,
7
7
  blockHashName: string,
8
- cleanUpRawEventFieldsInPlace: JSON.t => unit,
9
8
  /** Method name that the block handler is exposed under on the public
10
9
  `indexer` object — `"onBlock"` for chain-based ecosystems, `"onSlot"`
11
10
  for SVM. Centralised here so adding a new ecosystem only requires a
package/src/Envio.res CHANGED
@@ -42,14 +42,24 @@ type svmInstructionParams = {
42
42
  type svmTokenBalance = {
43
43
  account?: SvmTypes.Pubkey.t,
44
44
  mint?: SvmTypes.Pubkey.t,
45
+ /** Owner at the end of the transaction, falling back to the owner on entry
46
+ when the account was closed during it. Pre and post owners differ only when
47
+ a `SetAuthority(AccountOwner)` runs mid-transaction. */
45
48
  owner?: SvmTypes.Pubkey.t,
46
- preAmount?: string,
47
- postAmount?: string,
49
+ /** Mint decimals, for scaling the raw amounts below. */
50
+ decimals?: int,
51
+ /** Raw amount in base units before the transaction. Absent when the token
52
+ account was created during it. */
53
+ preAmount?: bigint,
54
+ /** Raw amount in base units after the transaction. Absent when the token
55
+ account was closed during it. */
56
+ postAmount?: bigint,
48
57
  }
49
58
 
50
59
  type svmTransaction = {
51
60
  transactionIndex?: int,
52
- signatures: array<string>,
61
+ /** The transaction's identifying signature (`allSignatures[0]`). */
62
+ signature: string,
53
63
  feePayer?: SvmTypes.Pubkey.t,
54
64
  success?: bool,
55
65
  err?: string,
@@ -58,6 +68,9 @@ type svmTransaction = {
58
68
  accountKeys: array<SvmTypes.Pubkey.t>,
59
69
  recentBlockhash?: string,
60
70
  version?: string,
71
+ /** Every signature on the transaction, in message order; the first is
72
+ `signature`. Longer than one element only for a multi-signer transaction. */
73
+ allSignatures: array<string>,
61
74
  tokenBalances?: array<svmTokenBalance>,
62
75
  }
63
76