envio 3.6.1 → 3.7.0-subgraph

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 (114) 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 +18 -2
  11. package/src/Config.res.mjs +9 -6
  12. package/src/Core.res +20 -0
  13. package/src/Core.res.mjs +11 -0
  14. package/src/Ecosystem.res +0 -1
  15. package/src/Envio.res +16 -3
  16. package/src/EventConfigBuilder.res +155 -40
  17. package/src/EventConfigBuilder.res.mjs +76 -24
  18. package/src/EventProcessing.res +10 -14
  19. package/src/EventProcessing.res.mjs +12 -10
  20. package/src/EventUtils.res +3 -47
  21. package/src/FetchState.res +124 -104
  22. package/src/FetchState.res.mjs +116 -100
  23. package/src/HandlerLoader.res +20 -8
  24. package/src/HandlerLoader.res.mjs +11 -2
  25. package/src/HandlerRegister.res +108 -2
  26. package/src/HandlerRegister.res.mjs +59 -10
  27. package/src/HandlerRegister.resi +4 -0
  28. package/src/InMemoryStore.res +1 -6
  29. package/src/InMemoryStore.res.mjs +1 -3
  30. package/src/Internal.res +84 -24
  31. package/src/Internal.res.mjs +33 -2
  32. package/src/LazyLoader.res +12 -11
  33. package/src/LazyLoader.res.mjs +13 -7
  34. package/src/Main.res +22 -9
  35. package/src/Main.res.mjs +18 -5
  36. package/src/MemoryStorage.res +756 -0
  37. package/src/MemoryStorage.res.mjs +623 -0
  38. package/src/Persistence.res +4 -2
  39. package/src/PgStorage.res +24 -8
  40. package/src/PgStorage.res.mjs +15 -12
  41. package/src/ReorgDetection.res +0 -222
  42. package/src/ReorgDetection.res.mjs +0 -163
  43. package/src/Rollback.res +14 -15
  44. package/src/Rollback.res.mjs +4 -5
  45. package/src/SimulateItems.res +2 -2
  46. package/src/UserContext.res +358 -51
  47. package/src/UserContext.res.mjs +271 -35
  48. package/src/Utils.res +3 -0
  49. package/src/bindings/ClickHouse.res +16 -11
  50. package/src/bindings/ClickHouse.res.mjs +10 -10
  51. package/src/bindings/Vitest.res +1 -1
  52. package/src/sources/BlockStore.res +115 -5
  53. package/src/sources/BlockStore.res.mjs +25 -0
  54. package/src/sources/Evm.res +0 -1
  55. package/src/sources/Evm.res.mjs +0 -1
  56. package/src/sources/EvmHyperSyncSource.res +19 -84
  57. package/src/sources/EvmHyperSyncSource.res.mjs +24 -60
  58. package/src/sources/Fuel.res +24 -4
  59. package/src/sources/Fuel.res.mjs +23 -3
  60. package/src/sources/FuelHyperSync.res +8 -3
  61. package/src/sources/FuelHyperSync.res.mjs +5 -4
  62. package/src/sources/FuelHyperSync.resi +3 -1
  63. package/src/sources/FuelHyperSyncClient.res +7 -10
  64. package/src/sources/FuelHyperSyncSource.res +18 -45
  65. package/src/sources/FuelHyperSyncSource.res.mjs +18 -35
  66. package/src/sources/HyperSync.res +49 -229
  67. package/src/sources/HyperSync.res.mjs +74 -191
  68. package/src/sources/HyperSync.resi +11 -35
  69. package/src/sources/HyperSyncClient.res +9 -79
  70. package/src/sources/HyperSyncClient.res.mjs +2 -6
  71. package/src/sources/RequestStat.res +5 -0
  72. package/src/sources/RequestStat.res.mjs +2 -0
  73. package/src/sources/RpcSource.res +149 -43
  74. package/src/sources/RpcSource.res.mjs +104 -41
  75. package/src/sources/SimulateSource.res +8 -5
  76. package/src/sources/SimulateSource.res.mjs +6 -6
  77. package/src/sources/Source.res +89 -16
  78. package/src/sources/Source.res.mjs +50 -1
  79. package/src/sources/SourceManager.res +255 -50
  80. package/src/sources/SourceManager.res.mjs +149 -55
  81. package/src/sources/SourceManager.resi +2 -6
  82. package/src/sources/Svm.res +0 -7
  83. package/src/sources/Svm.res.mjs +0 -8
  84. package/src/sources/SvmHyperSyncClient.res +21 -16
  85. package/src/sources/SvmHyperSyncClient.res.mjs +3 -4
  86. package/src/sources/SvmHyperSyncSource.res +25 -117
  87. package/src/sources/SvmHyperSyncSource.res.mjs +8 -121
  88. package/src/subgraph/blocks.ts +176 -0
  89. package/src/subgraph/calls.ts +217 -0
  90. package/src/subgraph/conformance.ts +99 -0
  91. package/src/subgraph/division.ts +110 -0
  92. package/src/subgraph/errors.ts +90 -0
  93. package/src/subgraph/graph-ts-types/VERSION +2 -0
  94. package/src/subgraph/graph-ts-types/chain/arweave.d.ts +70 -0
  95. package/src/subgraph/graph-ts-types/chain/cosmos.d.ts +327 -0
  96. package/src/subgraph/graph-ts-types/chain/ethereum.d.ts +233 -0
  97. package/src/subgraph/graph-ts-types/chain/near.d.ts +253 -0
  98. package/src/subgraph/graph-ts-types/chain/starknet.d.ts +32 -0
  99. package/src/subgraph/graph-ts-types/common/collections.d.ts +136 -0
  100. package/src/subgraph/graph-ts-types/common/conversion.d.ts +11 -0
  101. package/src/subgraph/graph-ts-types/common/datasource.d.ts +30 -0
  102. package/src/subgraph/graph-ts-types/common/eager-offset.d.ts +0 -0
  103. package/src/subgraph/graph-ts-types/common/json.d.ts +17 -0
  104. package/src/subgraph/graph-ts-types/common/numbers.d.ts +120 -0
  105. package/src/subgraph/graph-ts-types/common/value.d.ts +120 -0
  106. package/src/subgraph/graph-ts-types/common/yaml.d.ts +90 -0
  107. package/src/subgraph/graph-ts-types/global/global.d.ts +194 -0
  108. package/src/subgraph/graph-ts-types/helper-functions.d.ts +22 -0
  109. package/src/subgraph/graph-ts-types/index.d.ts +102 -0
  110. package/src/subgraph/graph-ts.ts +1871 -0
  111. package/src/subgraph/hosts.ts +148 -0
  112. package/src/subgraph/runtime.ts +845 -0
  113. package/src/subgraph/scope.ts +63 -0
  114. 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
@@ -119,6 +119,9 @@ type t = {
119
119
  userEntities: array<Internal.entityConfig>,
120
120
  allEntities: array<Internal.entityConfig>,
121
121
  allEnums: array<Table.enumConfig<Table.enum>>,
122
+ // Set only in subgraph mode: the translated manifest, which is what makes
123
+ // the subgraph runtime take over handler registration.
124
+ subgraph: option<JSON.t>,
122
125
  }
123
126
 
124
127
  module EnvioAddresses = {
@@ -613,6 +616,7 @@ let publicConfigSchema = S.schema(s =>
613
616
  "svm": s.matches(S.option(publicConfigEcosystemSchema)),
614
617
  "enums": s.matches(S.option(S.dict(S.array(S.string)))),
615
618
  "entities": s.matches(S.option(S.array(entityJsonSchema))),
619
+ "subgraph": s.matches(S.option(S.json(~validate=false))),
616
620
  }
617
621
  )
618
622
 
@@ -1016,8 +1020,15 @@ let fromPublic = (publicConfigJson: JSON.t) => {
1016
1020
  endBlock: ?publicChainConfig["endBlock"],
1017
1021
  maxReorgDepth: switch ecosystemName {
1018
1022
  | Ecosystem.Evm => publicChainConfig["maxReorgDepth"]->Option.getOr(200)
1019
-
1020
- | Ecosystem.Fuel | Ecosystem.Svm => 0
1023
+ // Tower BFT roots a block once 32 votes lock it in (MAX_LOCKOUT_HISTORY
1024
+ // is 31, plus the slot itself), which is what `finalized` waits for — so
1025
+ // 32 is the protocol's own bound on how far a fork can be replaced. That
1026
+ // bound counts *blocks* while the threshold here is measured in slot
1027
+ // numbers, and skipped slots make the slot distance the larger of the
1028
+ // two, so the default keeps a wide margin over it. Slots are ~0.4s, so
1029
+ // even this spans well under two minutes of chain.
1030
+ | Ecosystem.Svm => publicChainConfig["maxReorgDepth"]->Option.getOr(200)
1031
+ | Ecosystem.Fuel => 0
1021
1032
  },
1022
1033
  blockLag: publicChainConfig["blockLag"]->Option.getOr(0),
1023
1034
  contracts,
@@ -1110,6 +1121,7 @@ let fromPublic = (publicConfigJson: JSON.t) => {
1110
1121
  userEntities,
1111
1122
  allEntities,
1112
1123
  allEnums,
1124
+ subgraph: publicConfig["subgraph"],
1113
1125
  }
1114
1126
  }
1115
1127
 
@@ -1246,6 +1258,10 @@ let stripSensitiveData = (json: JSON.t): JSON.t => {
1246
1258
  stripChains(obj->Dict.get("evm"))
1247
1259
  stripChains(obj->Dict.get("fuel"))
1248
1260
  stripChains(obj->Dict.get("svm"))
1261
+ // The subgraph blob is how the runtime finds mappings. None of it
1262
+ // describes stored data — that's already in `evm` / `entities` — so a
1263
+ // specVersion bump or a mapping-path edit must not force a reset.
1264
+ obj->Utils.Dict.deleteInPlace("subgraph")
1249
1265
  }
1250
1266
  | _ => ()
1251
1267
  }
@@ -433,7 +433,8 @@ let publicConfigSchema = S$RescriptSchema.schema(s => ({
433
433
  fuel: s.m(S$RescriptSchema.option(publicConfigEcosystemSchema)),
434
434
  svm: s.m(S$RescriptSchema.option(publicConfigEcosystemSchema)),
435
435
  enums: s.m(S$RescriptSchema.option(S$RescriptSchema.dict(S$RescriptSchema.array(S$RescriptSchema.string)))),
436
- entities: s.m(S$RescriptSchema.option(S$RescriptSchema.array(entityJsonSchema)))
436
+ entities: s.m(S$RescriptSchema.option(S$RescriptSchema.array(entityJsonSchema))),
437
+ subgraph: s.m(S$RescriptSchema.option(S$RescriptSchema.json(false)))
437
438
  }));
438
439
 
439
440
  function fromPublic(publicConfigJson) {
@@ -685,13 +686,13 @@ function fromPublic(publicConfigJson) {
685
686
  }
686
687
  let tmp;
687
688
  switch (ecosystemName) {
688
- case "evm" :
689
- tmp = Stdlib_Option.getOr(publicChainConfig.maxReorgDepth, 200);
690
- break;
691
689
  case "fuel" :
692
- case "svm" :
693
690
  tmp = 0;
694
691
  break;
692
+ case "evm" :
693
+ case "svm" :
694
+ tmp = Stdlib_Option.getOr(publicChainConfig.maxReorgDepth, 200);
695
+ break;
695
696
  }
696
697
  return {
697
698
  name: chainName,
@@ -757,7 +758,8 @@ function fromPublic(publicConfigJson) {
757
758
  userEntitiesByName: userEntitiesByName,
758
759
  userEntities: userEntities,
759
760
  allEntities: allEntities,
760
- allEnums: allEnums
761
+ allEnums: allEnums,
762
+ subgraph: publicConfig.subgraph
761
763
  };
762
764
  }
763
765
 
@@ -902,6 +904,7 @@ function stripSensitiveData(json) {
902
904
  stripChains(cloned["evm"]);
903
905
  stripChains(cloned["fuel"]);
904
906
  stripChains(cloned["svm"]);
907
+ Utils.Dict.deleteInPlace(cloned, "subgraph");
905
908
  }
906
909
  return cloned;
907
910
  }
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>,
@@ -24,8 +26,16 @@ type fromUserApiResult = {
24
26
  indexerCode: Null.t<string>,
25
27
  }
26
28
 
29
+ type fromSubgraphOptions = {
30
+ name?: string,
31
+ env?: dict<string>,
32
+ files?: dict<string>,
33
+ root?: string,
34
+ }
35
+
27
36
  type addon = {
28
37
  getConfigJson: (~configPath: Null.t<string>, ~directory: Null.t<string>) => string,
38
+ fromSubgraph: (string, string, fromSubgraphOptions) => fromUserApiResult,
29
39
  encodeIndexedTopic: (~abiType: string, ~value: unknown) => EvmTypes.Hex.t,
30
40
  fromUserApi: (string, fromUserApiOptions) => fromUserApiResult,
31
41
  runCli: (~args: array<string>, ~envioPackageDir: Null.t<string>) => promise<Null.t<string>>,
@@ -43,6 +53,8 @@ type addon = {
43
53
  blockStore: blockStoreCtor,
44
54
  @as("AddressStore")
45
55
  addressStore: addressStoreCtor,
56
+ @as("MockHyperSyncServer")
57
+ mockHyperSyncServer: mockHyperSyncServerCtor,
46
58
  // Ordered transaction-field names exposed for the field-code contract test
47
59
  // (the ReScript `transactionFields` arrays must match the Rust ordinals).
48
60
  evmTransactionFieldNames: unit => array<string>,
@@ -51,6 +63,7 @@ type addon = {
51
63
  // must match the Rust ordinals).
52
64
  evmBlockFieldNames: unit => array<string>,
53
65
  svmBlockFieldNames: unit => array<string>,
66
+ fuelBlockFieldNames: unit => array<string>,
54
67
  }
55
68
 
56
69
  @module("node:module") external createRequire: string => {..} = "createRequire"
@@ -232,6 +245,13 @@ let fromUserApi = (~schema=?, ~env=?, ~files=?, ~withIndexerTypes=false, yaml) =
232
245
  )
233
246
  }
234
247
 
248
+ /// Parses a subgraph project without touching the filesystem: subgraph.yaml and
249
+ /// its schema inline, ABI bodies through `files`.
250
+ let fromSubgraph = (~name=?, ~env=?, ~files=?, ~root=?, ~manifest, ~schema) => {
251
+ let addon = getAddon()
252
+ addon.fromSubgraph(manifest, schema, {?name, ?env, ?files, ?root})
253
+ }
254
+
235
255
  let runCli = args => {
236
256
  let addon = getAddon()
237
257
  addon.runCli(~args, ~envioPackageDir=Null.make(envioPackageDir))
package/src/Core.res.mjs CHANGED
@@ -191,6 +191,16 @@ function fromUserApi(schema, env, files, withIndexerTypesOpt, yaml) {
191
191
  });
192
192
  }
193
193
 
194
+ function fromSubgraph(name, env, files, root, manifest, schema) {
195
+ let addon = getAddon();
196
+ return addon.fromSubgraph(manifest, schema, {
197
+ name: name,
198
+ env: env,
199
+ files: files,
200
+ root: root
201
+ });
202
+ }
203
+
194
204
  function runCli(args) {
195
205
  let addon = getAddon();
196
206
  return addon.runCli(args, envioPackageDir);
@@ -208,6 +218,7 @@ export {
208
218
  getAddon,
209
219
  getConfigJson,
210
220
  fromUserApi,
221
+ fromSubgraph,
211
222
  runCli,
212
223
  }
213
224
  /* envioPackageDir Not a pure module */
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