envio 3.5.0-alpha.3 → 3.5.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 (127) hide show
  1. package/package.json +6 -6
  2. package/src/Batch.res +8 -8
  3. package/src/Batch.res.mjs +4 -3
  4. package/src/BatchProcessing.res +17 -7
  5. package/src/BatchProcessing.res.mjs +8 -4
  6. package/src/ChainFetching.res +20 -19
  7. package/src/ChainFetching.res.mjs +14 -14
  8. package/src/ChainId.res +58 -0
  9. package/src/ChainId.res.mjs +49 -0
  10. package/src/ChainId.resi +27 -0
  11. package/src/ChainMap.res +12 -26
  12. package/src/ChainMap.res.mjs +5 -41
  13. package/src/ChainMap.resi +5 -20
  14. package/src/ChainMetadata.res +1 -1
  15. package/src/ChainMetadata.res.mjs +3 -2
  16. package/src/ChainState.res +35 -20
  17. package/src/ChainState.res.mjs +34 -11
  18. package/src/ChainState.resi +2 -0
  19. package/src/Config.res +41 -24
  20. package/src/Config.res.mjs +17 -14
  21. package/src/ContractRegisterContext.res +1 -1
  22. package/src/ContractRegisterContext.res.mjs +1 -1
  23. package/src/Core.res +1 -0
  24. package/src/CrossChainState.res +61 -16
  25. package/src/CrossChainState.res.mjs +37 -13
  26. package/src/CrossChainState.resi +3 -2
  27. package/src/Env.res +0 -3
  28. package/src/Env.res.mjs +0 -3
  29. package/src/EventConfigBuilder.res +1 -1
  30. package/src/EventProcessing.res +4 -4
  31. package/src/EventProcessing.res.mjs +5 -5
  32. package/src/FetchState.res +261 -89
  33. package/src/FetchState.res.mjs +187 -63
  34. package/src/FinalizeBackfill.res +33 -6
  35. package/src/FinalizeBackfill.res.mjs +19 -1
  36. package/src/HandlerRegister.res +8 -8
  37. package/src/HandlerRegister.res.mjs +5 -4
  38. package/src/HandlerRegister.resi +1 -1
  39. package/src/InMemoryStore.res +4 -0
  40. package/src/InMemoryStore.res.mjs +1 -1
  41. package/src/IndexerLoop.res +9 -2
  42. package/src/IndexerLoop.res.mjs +8 -1
  43. package/src/IndexerState.res +41 -24
  44. package/src/IndexerState.res.mjs +36 -17
  45. package/src/IndexerState.resi +9 -7
  46. package/src/Internal.res +19 -17
  47. package/src/Internal.res.mjs +18 -9
  48. package/src/LoadLayer.res +1 -1
  49. package/src/LoadLayer.res.mjs +2 -1
  50. package/src/LogSelection.res +2 -2
  51. package/src/Main.res +7 -11
  52. package/src/Main.res.mjs +4 -6
  53. package/src/Metrics.res +6 -6
  54. package/src/Metrics.res.mjs +4 -3
  55. package/src/Persistence.res +15 -7
  56. package/src/PgStorage.res +284 -258
  57. package/src/PgStorage.res.mjs +181 -208
  58. package/src/RawEvent.res +1 -2
  59. package/src/RawEvent.res.mjs +1 -1
  60. package/src/Rollback.res +18 -20
  61. package/src/Rollback.res.mjs +9 -9
  62. package/src/RollbackCommit.res +2 -2
  63. package/src/RollbackCommit.res.mjs +3 -3
  64. package/src/SafeCheckpointTracking.res +2 -2
  65. package/src/SimulateDeadInputTracker.res +17 -21
  66. package/src/SimulateDeadInputTracker.res.mjs +9 -8
  67. package/src/SimulateItems.res +8 -10
  68. package/src/SimulateItems.res.mjs +9 -10
  69. package/src/Sink.res +2 -2
  70. package/src/Sink.res.mjs +3 -2
  71. package/src/TestIndexer.res +11 -14
  72. package/src/TestIndexer.res.mjs +14 -10
  73. package/src/UserContext.res +2 -2
  74. package/src/UserContext.res.mjs +2 -1
  75. package/src/Utils.res +0 -4
  76. package/src/Utils.res.mjs +1 -6
  77. package/src/bindings/ClickHouse.res +20 -2
  78. package/src/bindings/ClickHouse.res.mjs +25 -14
  79. package/src/db/EntityHistory.res +1 -1
  80. package/src/db/IndexCatalog.res +248 -0
  81. package/src/db/IndexCatalog.res.mjs +222 -0
  82. package/src/db/IndexDefinition.res +131 -0
  83. package/src/db/IndexDefinition.res.mjs +138 -0
  84. package/src/db/IndexManager.res +145 -0
  85. package/src/db/IndexManager.res.mjs +130 -0
  86. package/src/db/InternalTable.res +50 -26
  87. package/src/db/InternalTable.res.mjs +32 -24
  88. package/src/db/Table.res +11 -1
  89. package/src/db/Table.res.mjs +9 -4
  90. package/src/sources/AddressSet.res +5 -4
  91. package/src/sources/AddressStore.res +25 -14
  92. package/src/sources/AddressStore.res.mjs +10 -12
  93. package/src/sources/Evm.res +2 -2
  94. package/src/sources/Evm.res.mjs +1 -1
  95. package/src/sources/EvmChain.res +3 -3
  96. package/src/sources/EvmChain.res.mjs +3 -3
  97. package/src/sources/EvmHyperSyncSource.res +7 -7
  98. package/src/sources/EvmHyperSyncSource.res.mjs +6 -6
  99. package/src/sources/Fuel.res +2 -2
  100. package/src/sources/Fuel.res.mjs +1 -1
  101. package/src/sources/FuelHyperSyncClient.res +3 -0
  102. package/src/sources/FuelHyperSyncClient.res.mjs +1 -0
  103. package/src/sources/FuelHyperSyncSource.res +5 -7
  104. package/src/sources/FuelHyperSyncSource.res.mjs +5 -5
  105. package/src/sources/HyperSync.resi +2 -2
  106. package/src/sources/HyperSyncClient.res +5 -0
  107. package/src/sources/HyperSyncClient.res.mjs +1 -0
  108. package/src/sources/RpcSource.res +13 -14
  109. package/src/sources/RpcSource.res.mjs +13 -12
  110. package/src/sources/SimulateSource.res +42 -15
  111. package/src/sources/SimulateSource.res.mjs +27 -9
  112. package/src/sources/Source.res +1 -5
  113. package/src/sources/SourceManager.res +12 -12
  114. package/src/sources/SourceManager.res.mjs +11 -10
  115. package/src/sources/SourceManager.resi +2 -2
  116. package/src/sources/Svm.res +4 -5
  117. package/src/sources/Svm.res.mjs +5 -4
  118. package/src/sources/SvmHyperSyncClient.res +3 -0
  119. package/src/sources/SvmHyperSyncClient.res.mjs +1 -0
  120. package/src/sources/SvmHyperSyncSource.res +4 -4
  121. package/src/sources/SvmHyperSyncSource.res.mjs +3 -3
  122. package/src/tui/Tui.res +1 -1
  123. package/src/tui/Tui.res.mjs +2 -1
  124. package/src/tui/components/CustomHooks.res +5 -4
  125. package/src/tui/components/CustomHooks.res.mjs +4 -2
  126. package/src/db/IndexRegistry.res +0 -219
  127. package/src/db/IndexRegistry.res.mjs +0 -195
@@ -1,11 +1,15 @@
1
- let make = (~items: array<Internal.item>, ~endBlock: int, ~chain: ChainMap.Chain.t): Source.t => {
1
+ let make = (
2
+ ~items: array<Internal.item>,
3
+ ~endBlock: int,
4
+ ~chainId: ChainId.t,
5
+ ~addressStore: AddressStore.t,
6
+ ): Source.t => {
2
7
  let reportedHeight = max(endBlock, 1)
3
8
 
4
9
  {
5
10
  name: "SimulateSource",
6
- simulateItems: items,
7
11
  sourceFor: Sync,
8
- chain,
12
+ chainId,
9
13
  poweredByHyperSync: false,
10
14
  pollingInterval: 0,
11
15
  getBlockHashes: (~blockNumbers as _, ~logger as _) => {
@@ -27,8 +31,9 @@ let make = (~items: array<Internal.item>, ~endBlock: int, ~chain: ChainMap.Chain
27
31
  ~logger as _,
28
32
  ) => {
29
33
  // Mirror a real backend: return only the items this query would match —
30
- // in the block range, part of the selection, and passing the same address
31
- // gates a real source applies natively while routing (the emitter must be
34
+ // in the block range, part of the selection, at or after the
35
+ // registration's own start block, and passing the same address gates a
36
+ // real source applies natively while routing (the emitter must be
32
37
  // registered for the event's contract at or before the item's block, and
33
38
  // any address-valued param filter must hold). Overlapping queries may
34
39
  // return the same item more than once; the buffer dedups it.
@@ -36,27 +41,49 @@ let make = (~items: array<Internal.item>, ~endBlock: int, ~chain: ChainMap.Chain
36
41
  | Some(toBlock) => toBlock
37
42
  | None => reportedHeight
38
43
  }
39
- let selectionEventIds = Utils.Set.make()
44
+ // By registration index, not `eventConfig.id`: two registrations of the
45
+ // same event are independent and may sit in different partitions, so
46
+ // matching on the shared event id would let a sibling's items leak into
47
+ // this query.
48
+ let selectionRegistrationIndexes = Utils.Set.make()
40
49
  selection.onEventRegistrations->Array.forEach(reg =>
41
- selectionEventIds->Utils.Set.add(reg.eventConfig.id)->ignore
50
+ selectionRegistrationIndexes->Utils.Set.add(reg.index)->ignore
42
51
  )
43
52
 
53
+ // A client-filtered contract is queried address-free, so this partition
54
+ // holds none of its addresses and only the chain-wide store can answer
55
+ // for it — exactly the fallback every real source's router makes.
56
+ let isAddressAllowed = (address, ~contractName, ~blockNumber) =>
57
+ switch selection.clientFilteredContracts {
58
+ | Some(contractNames) if contractNames->Array.includes(contractName) =>
59
+ addressStore->AddressStore.isIndexedAt(address, contractName, blockNumber)
60
+ | _ => addressSet->AddressSet.containsAt(address, contractName, blockNumber)
61
+ }
62
+
63
+ // The registration's own start block, which the contract-wide address gate
64
+ // can't express. Every native router applies it while routing.
65
+ let hasStarted = (onEventRegistration: Internal.onEventRegistration, ~blockNumber) =>
66
+ switch onEventRegistration.startBlock {
67
+ | Some(startBlock) => blockNumber >= startBlock
68
+ | None => true
69
+ }
70
+
44
71
  let parsedQueueItems = items->Array.filter(item => {
45
72
  let eventItem = item->Internal.castUnsafeEventItem
46
73
  let {blockNumber, onEventRegistration} = eventItem
47
74
  if blockNumber < fromBlock || blockNumber > toBlockQueried {
48
75
  false
49
- } else if !(selectionEventIds->Utils.Set.has(onEventRegistration.eventConfig.id)) {
76
+ } else if !(selectionRegistrationIndexes->Utils.Set.has(onEventRegistration.index)) {
77
+ false
78
+ } else if !(onEventRegistration->hasStarted(~blockNumber)) {
50
79
  false
51
80
  } else {
52
81
  let contractName = onEventRegistration.eventConfig.contractName
53
82
  let emitterAllowed =
54
83
  onEventRegistration.isWildcard ||
55
- addressSet->AddressSet.has(
56
- eventItem.payload->Internal.getPayloadSrcAddress,
57
- contractName,
58
- blockNumber,
59
- )
84
+ eventItem.payload
85
+ ->Internal.getPayloadSrcAddress
86
+ ->isAddressAllowed(~contractName, ~blockNumber)
60
87
  emitterAllowed &&
61
88
  switch onEventRegistration.addressFilterParamGroups {
62
89
  | None
@@ -64,8 +91,8 @@ let make = (~items: array<Internal.item>, ~endBlock: int, ~chain: ChainMap.Chain
64
91
  | Some(groups) =>
65
92
  let params = eventItem.payload->Internal.getPayloadAddressParams
66
93
  groups->Array.some(group =>
67
- group->Array.every(name =>
68
- addressSet->AddressSet.has(params->Dict.getUnsafe(name), contractName, blockNumber)
94
+ group->Array.every(
95
+ name => params->Dict.getUnsafe(name)->isAddressAllowed(~contractName, ~blockNumber),
69
96
  )
70
97
  )
71
98
  }
@@ -2,12 +2,12 @@
2
2
 
3
3
  import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
4
4
 
5
- function make(items, endBlock, chain) {
5
+ function make(items, endBlock, chainId, addressStore) {
6
6
  let reportedHeight = Primitive_int.max(endBlock, 1);
7
7
  return {
8
8
  name: "SimulateSource",
9
9
  sourceFor: "Sync",
10
- chain: chain,
10
+ chainId: chainId,
11
11
  poweredByHyperSync: false,
12
12
  pollingInterval: 0,
13
13
  getBlockHashes: (param, param$1) => Promise.resolve({
@@ -23,21 +23,40 @@ function make(items, endBlock, chain) {
23
23
  }),
24
24
  getItemsOrThrow: (fromBlock, toBlock, addressSet, param, param$1, selection, param$2, param$3, param$4) => {
25
25
  let toBlockQueried = toBlock !== undefined ? toBlock : reportedHeight;
26
- let selectionEventIds = new Set();
26
+ let selectionRegistrationIndexes = new Set();
27
27
  selection.onEventRegistrations.forEach(reg => {
28
- selectionEventIds.add(reg.eventConfig.id);
28
+ selectionRegistrationIndexes.add(reg.index);
29
29
  });
30
+ let isAddressAllowed = (address, contractName, blockNumber) => {
31
+ let contractNames = selection.clientFilteredContracts;
32
+ if (contractNames !== undefined && contractNames.includes(contractName)) {
33
+ return addressStore.isIndexedAt(address, contractName, blockNumber);
34
+ } else {
35
+ return addressSet.containsAt(address, contractName, blockNumber);
36
+ }
37
+ };
38
+ let hasStarted = (onEventRegistration, blockNumber) => {
39
+ let startBlock = onEventRegistration.startBlock;
40
+ if (startBlock !== undefined) {
41
+ return blockNumber >= startBlock;
42
+ } else {
43
+ return true;
44
+ }
45
+ };
30
46
  let parsedQueueItems = items.filter(item => {
31
47
  let blockNumber = item.blockNumber;
32
48
  if (blockNumber < fromBlock || blockNumber > toBlockQueried) {
33
49
  return false;
34
50
  }
35
51
  let onEventRegistration = item.onEventRegistration;
36
- if (!selectionEventIds.has(onEventRegistration.eventConfig.id)) {
52
+ if (!selectionRegistrationIndexes.has(onEventRegistration.index)) {
53
+ return false;
54
+ }
55
+ if (!hasStarted(onEventRegistration, blockNumber)) {
37
56
  return false;
38
57
  }
39
58
  let contractName = onEventRegistration.eventConfig.contractName;
40
- let emitterAllowed = onEventRegistration.isWildcard || addressSet.has(item.payload.srcAddress, contractName, blockNumber);
59
+ let emitterAllowed = onEventRegistration.isWildcard || isAddressAllowed(item.payload.srcAddress, contractName, blockNumber);
41
60
  if (!emitterAllowed) {
42
61
  return false;
43
62
  }
@@ -49,7 +68,7 @@ function make(items, endBlock, chain) {
49
68
  return true;
50
69
  }
51
70
  let params = item.payload.params;
52
- return groups.some(group => group.every(name => addressSet.has(params[name], contractName, blockNumber)));
71
+ return groups.some(group => group.every(name => isAddressAllowed(params[name], contractName, blockNumber)));
53
72
  });
54
73
  return Promise.resolve({
55
74
  knownHeight: reportedHeight,
@@ -65,8 +84,7 @@ function make(items, endBlock, chain) {
65
84
  },
66
85
  requestStats: []
67
86
  });
68
- },
69
- simulateItems: items
87
+ }
70
88
  };
71
89
  }
72
90
 
@@ -64,7 +64,7 @@ type sourceFor = Sync | Fallback | Realtime
64
64
  type t = {
65
65
  name: string,
66
66
  sourceFor: sourceFor,
67
- chain: ChainMap.Chain.t,
67
+ chainId: ChainId.t,
68
68
  poweredByHyperSync: bool,
69
69
  /* Frequency (in ms) used when polling for new events on this network. */
70
70
  pollingInterval: int,
@@ -95,8 +95,4 @@ type t = {
95
95
  // Invoked by SourceManager once a rollback target is known so the source can
96
96
  // drop any state that may now point at an orphaned chain (e.g. RPC block cache).
97
97
  onReorg?: (~rollbackTargetBlock: int) => unit,
98
- // Present only on the simulate source: the items a test fed in. The chain
99
- // tracks which of these never reach a handler so the run can report dead
100
- // simulate inputs on completion.
101
- simulateItems?: array<Internal.item>,
102
98
  }
@@ -32,7 +32,7 @@ let recordRequestStats = (sourceState: sourceState, requestStats: array<Source.r
32
32
  // inline into the /metrics response.
33
33
  type requestStatSample = {
34
34
  sourceName: string,
35
- chainId: int,
35
+ chainId: ChainId.t,
36
36
  method: string,
37
37
  count: int,
38
38
  seconds: float,
@@ -81,7 +81,7 @@ let getActiveSource = sourceManager => sourceManager.activeSource
81
81
  let getRequestStatSamples = (sourceManager: t): array<requestStatSample> => {
82
82
  let samples = []
83
83
  sourceManager.sourcesState->Array.forEach(sourceState => {
84
- let chainId = sourceState.source.chain->ChainMap.Chain.toChainId
84
+ let chainId = sourceState.source.chainId
85
85
  sourceState.requestStats->Utils.Dict.forEachWithKey((agg, method) => {
86
86
  samples
87
87
  ->Array.push({
@@ -101,7 +101,7 @@ let getRequestStatSamples = (sourceManager: t): array<requestStatSample> => {
101
101
  // observed height yet are skipped.
102
102
  type sourceHeightSample = {
103
103
  sourceName: string,
104
- chainId: int,
104
+ chainId: ChainId.t,
105
105
  height: int,
106
106
  }
107
107
 
@@ -111,7 +111,7 @@ let getSourceHeightSamples = (sourceManager: t): array<sourceHeightSample> => {
111
111
  if sourceState.knownHeight > 0 {
112
112
  samples->Array.push({
113
113
  sourceName: sourceState.source.name,
114
- chainId: sourceState.source.chain->ChainMap.Chain.toChainId,
114
+ chainId: sourceState.source.chainId,
115
115
  height: sourceState.knownHeight,
116
116
  })
117
117
  }
@@ -425,7 +425,7 @@ let getSourceNewHeight = async (
425
425
  logger->Logging.childTrace({
426
426
  "msg": "onHeight subscription stale, switching to polling fallback",
427
427
  "source": source.name,
428
- "chainId": source.chain->ChainMap.Chain.toChainId,
428
+ "chainId": source.chainId,
429
429
  })
430
430
  let h = ref(initialHeight)
431
431
  while h.contents <= knownHeight && !(newHeight.contents > initialHeight) {
@@ -585,7 +585,7 @@ let waitForNewBlock = async (sourceManager: t, ~knownHeight, ~isRealtime, ~reduc
585
585
 
586
586
  let logger = Logging.createChild(
587
587
  ~params={
588
- "chainId": sourceManager.activeSource.chain->ChainMap.Chain.toChainId,
588
+ "chainId": sourceManager.activeSource.chainId,
589
589
  "knownHeight": knownHeight,
590
590
  },
591
591
  )
@@ -725,7 +725,7 @@ let executeQuery = async (
725
725
  | Some(s) =>
726
726
  if s.source !== sourceManager.activeSource {
727
727
  let logger = Logging.createChild(
728
- ~params={"chainId": sourceManager.activeSource.chain->ChainMap.Chain.toChainId},
728
+ ~params={"chainId": sourceManager.activeSource.chainId},
729
729
  )
730
730
  logger->Logging.childInfo({
731
731
  "msg": "Switching data-source",
@@ -737,7 +737,7 @@ let executeQuery = async (
737
737
  s
738
738
  | None =>
739
739
  let logger = Logging.createChild(
740
- ~params={"chainId": sourceManager.activeSource.chain->ChainMap.Chain.toChainId},
740
+ ~params={"chainId": sourceManager.activeSource.chainId},
741
741
  )
742
742
  %raw(`null`)->ErrorHandling.mkLogAndRaise(~logger, ~msg=noSourcesError)
743
743
  }
@@ -748,7 +748,7 @@ let executeQuery = async (
748
748
 
749
749
  let logger = Logging.createChild(
750
750
  ~params={
751
- "chainId": source.chain->ChainMap.Chain.toChainId,
751
+ "chainId": source.chainId,
752
752
  "logType": "Block Range Query",
753
753
  "partitionId": query.partitionId,
754
754
  "source": source.name,
@@ -766,7 +766,7 @@ let executeQuery = async (
766
766
  ~addressSet=query.addresses,
767
767
  ~partitionId=query.partitionId,
768
768
  ~knownHeight,
769
- ~selection=query.selection,
769
+ ~selection=query.selection->FetchState.narrowSelectionToRange(~toBlock),
770
770
  ~itemsTarget=query.itemsTarget,
771
771
  ~retry,
772
772
  ~logger,
@@ -896,7 +896,7 @@ let getBlockHashes = async (sourceManager: t, ~blockNumbers: array<int>, ~isReal
896
896
  | Some(s) => s
897
897
  | None =>
898
898
  let logger = Logging.createChild(
899
- ~params={"chainId": sourceManager.activeSource.chain->ChainMap.Chain.toChainId},
899
+ ~params={"chainId": sourceManager.activeSource.chainId},
900
900
  )
901
901
  %raw(`null`)->ErrorHandling.mkLogAndRaise(
902
902
  ~logger,
@@ -909,7 +909,7 @@ let getBlockHashes = async (sourceManager: t, ~blockNumbers: array<int>, ~isReal
909
909
 
910
910
  let logger = Logging.createChild(
911
911
  ~params={
912
- "chainId": source.chain->ChainMap.Chain.toChainId,
912
+ "chainId": source.chainId,
913
913
  "logType": "Block Hash Query",
914
914
  "source": source.name,
915
915
  "retry": retry,
@@ -3,6 +3,7 @@
3
3
  import * as Utils from "../Utils.res.mjs";
4
4
  import * as Source from "./Source.res.mjs";
5
5
  import * as Logging from "../Logging.res.mjs";
6
+ import * as FetchState from "../FetchState.res.mjs";
6
7
  import * as Performance from "../bindings/Performance.res.mjs";
7
8
  import * as ErrorHandling from "../ErrorHandling.res.mjs";
8
9
  import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
@@ -36,7 +37,7 @@ function getActiveSource(sourceManager) {
36
37
  function getRequestStatSamples(sourceManager) {
37
38
  let samples = [];
38
39
  sourceManager.sourcesState.forEach(sourceState => {
39
- let chainId = sourceState.source.chain;
40
+ let chainId = sourceState.source.chainId;
40
41
  Utils.Dict.forEachWithKey(sourceState.requestStats, (agg, method) => {
41
42
  samples.push({
42
43
  sourceName: sourceState.source.name,
@@ -56,7 +57,7 @@ function getSourceHeightSamples(sourceManager) {
56
57
  if (sourceState.knownHeight > 0) {
57
58
  samples.push({
58
59
  sourceName: sourceState.source.name,
59
- chainId: sourceState.source.chain,
60
+ chainId: sourceState.source.chainId,
60
61
  height: sourceState.knownHeight
61
62
  });
62
63
  return;
@@ -350,7 +351,7 @@ async function getSourceNewHeight(sourceManager, sourceState, knownHeight, stall
350
351
  Logging.childTrace(logger, {
351
352
  msg: "onHeight subscription stale, switching to polling fallback",
352
353
  source: source.name,
353
- chainId: source.chain
354
+ chainId: source.chainId
354
355
  });
355
356
  let h = initialHeight;
356
357
  while (h <= knownHeight && newHeight.contents <= initialHeight) {
@@ -506,7 +507,7 @@ function getNextSource(sourceManager, isRealtime, excludedSources) {
506
507
  async function waitForNewBlock(sourceManager, knownHeight, isRealtime, reducedPolling) {
507
508
  let sourcesState = sourceManager.sourcesState;
508
509
  let logger = Logging.createChild({
509
- chainId: sourceManager.activeSource.chain,
510
+ chainId: sourceManager.activeSource.chainId,
510
511
  knownHeight: knownHeight
511
512
  });
512
513
  if (!sourceManager.waitingLogged) {
@@ -569,7 +570,7 @@ async function executeQuery(sourceManager, query, knownHeight, isRealtime) {
569
570
  if (s !== undefined) {
570
571
  if (s.source !== sourceManager.activeSource) {
571
572
  let logger = Logging.createChild({
572
- chainId: sourceManager.activeSource.chain
573
+ chainId: sourceManager.activeSource.chainId
573
574
  });
574
575
  Logging.childInfo(logger, {
575
576
  msg: "Switching data-source",
@@ -581,7 +582,7 @@ async function executeQuery(sourceManager, query, knownHeight, isRealtime) {
581
582
  sourceState = s;
582
583
  } else {
583
584
  let logger$1 = Logging.createChild({
584
- chainId: sourceManager.activeSource.chain
585
+ chainId: sourceManager.activeSource.chainId
585
586
  });
586
587
  sourceState = ErrorHandling.mkLogAndRaise(logger$1, "The indexer doesn't have data-sources which can continue fetching. Please, check the error logs or reach out to the Envio team.", null);
587
588
  }
@@ -590,7 +591,7 @@ async function executeQuery(sourceManager, query, knownHeight, isRealtime) {
590
591
  let toBlock = toBlockRef;
591
592
  let retry = retryRef;
592
593
  let logger$2 = Logging.createChild({
593
- chainId: source.chain,
594
+ chainId: source.chainId,
594
595
  logType: "Block Range Query",
595
596
  partitionId: query.partitionId,
596
597
  source: source.name,
@@ -600,7 +601,7 @@ async function executeQuery(sourceManager, query, knownHeight, isRealtime) {
600
601
  retry: retry
601
602
  });
602
603
  try {
603
- let response = await source.getItemsOrThrow(query.fromBlock, toBlock, query.addresses, knownHeight, query.partitionId, query.selection, query.itemsTarget, retry, logger$2);
604
+ let response = await source.getItemsOrThrow(query.fromBlock, toBlock, query.addresses, knownHeight, query.partitionId, FetchState.narrowSelectionToRange(query.selection, toBlock), query.itemsTarget, retry, logger$2);
604
605
  recordRequestStats(sourceState, response.requestStats);
605
606
  sourceState.lastFailedAt = undefined;
606
607
  if (response.knownHeight > sourceState.knownHeight) {
@@ -725,7 +726,7 @@ async function getBlockHashes(sourceManager, blockNumbers, isRealtime) {
725
726
  sourceState = s;
726
727
  } else {
727
728
  let logger = Logging.createChild({
728
- chainId: sourceManager.activeSource.chain
729
+ chainId: sourceManager.activeSource.chainId
729
730
  });
730
731
  sourceState = ErrorHandling.mkLogAndRaise(logger, "No data-sources available for fetching block hashes.", null);
731
732
  }
@@ -733,7 +734,7 @@ async function getBlockHashes(sourceManager, blockNumbers, isRealtime) {
733
734
  let source = sourceState.source;
734
735
  let retry = retryRef;
735
736
  let logger$1 = Logging.createChild({
736
- chainId: source.chain,
737
+ chainId: source.chainId,
737
738
  logType: "Block Hash Query",
738
739
  source: source.name,
739
740
  retry: retry
@@ -23,7 +23,7 @@ let getActiveSource: t => Source.t
23
23
 
24
24
  type requestStatSample = {
25
25
  sourceName: string,
26
- chainId: int,
26
+ chainId: ChainId.t,
27
27
  method: string,
28
28
  count: int,
29
29
  seconds: float,
@@ -33,7 +33,7 @@ let getRequestStatSamples: t => array<requestStatSample>
33
33
 
34
34
  type sourceHeightSample = {
35
35
  sourceName: string,
36
- chainId: int,
36
+ chainId: ChainId.t,
37
37
  height: int,
38
38
  }
39
39
 
@@ -50,7 +50,7 @@ let make = (~logger: Pino.t): Ecosystem.t => {
50
50
  ~params={
51
51
  "program": eventItem.onEventRegistration.eventConfig.contractName,
52
52
  "instruction": eventItem.onEventRegistration.eventConfig.name,
53
- "chainId": eventItem.chain->ChainMap.Chain.toChainId,
53
+ "chainId": eventItem.chainId,
54
54
  "slot": eventItem.blockNumber,
55
55
  "programId": instruction.programId,
56
56
  },
@@ -70,14 +70,13 @@ module GetFinalizedSlot = {
70
70
  )
71
71
  }
72
72
 
73
- let makeRPCSource = (~chain, ~rpc: string, ~sourceFor: Source.sourceFor=Sync): Source.t => {
73
+ let makeRPCSource = (~chainId, ~rpc: string, ~sourceFor: Source.sourceFor=Sync): Source.t => {
74
74
  let client = Rest.client(rpc)
75
- let chainId = chain->ChainMap.Chain.toChainId
76
75
 
77
76
  let urlHost = switch Utils.Url.getHostFromUrl(rpc) {
78
77
  | None =>
79
78
  JsError.throwWithMessage(
80
- `The RPC url for chain ${chainId->Int.toString} is in incorrect format. The RPC url needs to start with either http:// or https://`,
79
+ `The RPC url for chain ${chainId->ChainId.toString} is in incorrect format. The RPC url needs to start with either http:// or https://`,
81
80
  )
82
81
  | Some(host) => host
83
82
  }
@@ -86,7 +85,7 @@ let makeRPCSource = (~chain, ~rpc: string, ~sourceFor: Source.sourceFor=Sync): S
86
85
  {
87
86
  name,
88
87
  sourceFor,
89
- chain,
88
+ chainId,
90
89
  poweredByHyperSync: false,
91
90
  pollingInterval: 10_000,
92
91
  getBlockHashes: (~blockNumbers as _, ~logger as _) =>
@@ -3,6 +3,7 @@
3
3
  import * as Rpc from "./Rpc.res.mjs";
4
4
  import * as Rest from "../vendored/Rest.res.mjs";
5
5
  import * as Utils from "../Utils.res.mjs";
6
+ import * as ChainId from "../ChainId.res.mjs";
6
7
  import * as Logging from "../Logging.res.mjs";
7
8
  import * as Internal from "../Internal.res.mjs";
8
9
  import * as BlockStore from "./BlockStore.res.mjs";
@@ -47,7 +48,7 @@ function make(logger) {
47
48
  return Logging.createChildFrom(logger, {
48
49
  program: eventItem.onEventRegistration.eventConfig.contractName,
49
50
  instruction: eventItem.onEventRegistration.eventConfig.name,
50
- chainId: eventItem.chain,
51
+ chainId: eventItem.chainId,
51
52
  slot: eventItem.blockNumber,
52
53
  programId: instruction.programId
53
54
  });
@@ -66,16 +67,16 @@ let GetFinalizedSlot = {
66
67
  route: route
67
68
  };
68
69
 
69
- function makeRPCSource(chain, rpc, sourceForOpt) {
70
+ function makeRPCSource(chainId, rpc, sourceForOpt) {
70
71
  let sourceFor = sourceForOpt !== undefined ? sourceForOpt : "Sync";
71
72
  let client = Rest.client(rpc, undefined);
72
73
  let host = Utils.Url.getHostFromUrl(rpc);
73
- let urlHost = host !== undefined ? host : Stdlib_JsError.throwWithMessage(`The RPC url for chain ` + chain.toString() + ` is in incorrect format. The RPC url needs to start with either http:// or https://`);
74
+ let urlHost = host !== undefined ? host : Stdlib_JsError.throwWithMessage(`The RPC url for chain ` + ChainId.toString(chainId) + ` is in incorrect format. The RPC url needs to start with either http:// or https://`);
74
75
  let name = `RPC (` + urlHost + `)`;
75
76
  return {
76
77
  name: name,
77
78
  sourceFor: sourceFor,
78
- chain: chain,
79
+ chainId: chainId,
79
80
  poweredByHyperSync: false,
80
81
  pollingInterval: 10000,
81
82
  getBlockHashes: (param, param$1) => Stdlib_JsError.throwWithMessage("Svm does not support getting block hashes"),
@@ -25,6 +25,8 @@ module Registration = {
25
25
  contractName: string,
26
26
  programId: string,
27
27
  isWildcard: bool,
28
+ // Earliest slot this registration accepts; `None` is unrestricted.
29
+ startBlock: option<int>,
28
30
  discriminator?: string,
29
31
  discriminatorByteLen: int,
30
32
  isInner?: bool,
@@ -52,6 +54,7 @@ module Registration = {
52
54
  contractName: eventConfig.contractName,
53
55
  programId: eventConfig.programId->SvmTypes.Pubkey.toString,
54
56
  isWildcard: reg.isWildcard,
57
+ startBlock: reg.startBlock,
55
58
  discriminator: ?eventConfig.discriminator,
56
59
  discriminatorByteLen: eventConfig.discriminatorByteLen,
57
60
  isInner: ?eventConfig.isInner,
@@ -18,6 +18,7 @@ function fromOnEventRegistrations(onEventRegistrations) {
18
18
  contractName: eventConfig.contractName,
19
19
  programId: eventConfig.programId,
20
20
  isWildcard: reg.isWildcard,
21
+ startBlock: reg.startBlock,
21
22
  discriminator: eventConfig.discriminator,
22
23
  discriminatorByteLen: eventConfig.discriminatorByteLen,
23
24
  isInner: eventConfig.isInner,
@@ -1,7 +1,7 @@
1
1
  open Source
2
2
 
3
3
  type options = {
4
- chain: ChainMap.Chain.t,
4
+ chainId: ChainId.t,
5
5
  endpointUrl: string,
6
6
  apiToken: option<string>,
7
7
  onEventRegistrations: array<Internal.svmOnEventRegistration>,
@@ -68,7 +68,7 @@ let toSvmInstruction = (
68
68
  }
69
69
 
70
70
  let make = (
71
- {chain, endpointUrl, apiToken, onEventRegistrations, clientTimeoutMillis, addressStore}: options,
71
+ {chainId, endpointUrl, apiToken, onEventRegistrations, clientTimeoutMillis, addressStore}: options,
72
72
  ): t => {
73
73
  let name = "SvmHyperSync"
74
74
 
@@ -159,7 +159,7 @@ let make = (
159
159
  )
160
160
  Internal.Event({
161
161
  onEventRegistration,
162
- chain,
162
+ chainId,
163
163
  blockNumber: item.slot,
164
164
  logIndex: synthLogIndex(
165
165
  ~transactionIndex=item.transactionIndex,
@@ -283,7 +283,7 @@ let make = (
283
283
  {
284
284
  name,
285
285
  sourceFor: Sync,
286
- chain,
286
+ chainId,
287
287
  pollingInterval: 1000,
288
288
  poweredByHyperSync: true,
289
289
  getBlockHashes,
@@ -57,7 +57,7 @@ function toSvmInstruction(item, programName, instructionName) {
57
57
 
58
58
  function make(param) {
59
59
  let onEventRegistrations = param.onEventRegistrations;
60
- let chain = param.chain;
60
+ let chainId = param.chainId;
61
61
  let client = SvmHyperSyncClient.make(param.endpointUrl, param.apiToken, param.clientTimeoutMillis, undefined, undefined, undefined, SvmHyperSyncClient.Registration.fromOnEventRegistrations(onEventRegistrations), param.addressStore);
62
62
  let getItemsOrThrow = async (fromBlock, toBlock, addressSet, knownHeight, param, selection, itemsTarget, retry, param$1) => {
63
63
  let totalTimeRef = Performance.now();
@@ -113,7 +113,7 @@ function make(param) {
113
113
  return {
114
114
  kind: 0,
115
115
  onEventRegistration: onEventRegistration,
116
- chain: chain,
116
+ chainId: chainId,
117
117
  blockNumber: item.slot,
118
118
  logIndex: synthLogIndex(item.transactionIndex, item.instructionAddress),
119
119
  transactionIndex: item.transactionIndex,
@@ -243,7 +243,7 @@ function make(param) {
243
243
  return {
244
244
  name: "SvmHyperSync",
245
245
  sourceFor: "Sync",
246
- chain: chain,
246
+ chainId: chainId,
247
247
  poweredByHyperSync: true,
248
248
  pollingInterval: 1000,
249
249
  getBlockHashes: getBlockHashes,
package/src/tui/Tui.res CHANGED
@@ -204,7 +204,7 @@ module App = {
204
204
  knownHeight: data.knownHeight,
205
205
  latestFetchedBlockNumber,
206
206
  eventsProcessed: numEventsProcessed,
207
- chainId: (cs->ChainState.chainConfig).id->Int.toString,
207
+ chainId: (cs->ChainState.chainConfig).id->ChainId.toString,
208
208
  progressBlock: committedProgressBlockNumber < data.startBlock
209
209
  ? Some(data.startBlock)
210
210
  : Some(committedProgressBlockNumber),
@@ -4,6 +4,7 @@ import * as Env from "../Env.res.mjs";
4
4
  import * as Ink from "./bindings/Ink.res.mjs";
5
5
  import * as Ink$1 from "ink";
6
6
  import * as React from "react";
7
+ import * as ChainId from "../ChainId.res.mjs";
7
8
  import * as SyncETA from "./components/SyncETA.res.mjs";
8
9
  import * as TuiData from "./components/TuiData.res.mjs";
9
10
  import * as Messages from "./components/Messages.res.mjs";
@@ -255,7 +256,7 @@ function Tui$App(props) {
255
256
  break;
256
257
  }
257
258
  return {
258
- chainId: ChainState.chainConfig(cs).id.toString(),
259
+ chainId: ChainId.toString(ChainState.chainConfig(cs).id),
259
260
  eventsProcessed: numEventsProcessed,
260
261
  progressBlock: committedProgressBlockNumber < data.startBlock ? data.startBlock : committedProgressBlockNumber,
261
262
  bufferBlock: latestFetchedBlockNumber,
@@ -4,16 +4,16 @@ module InitApi = {
4
4
  envioVersion: string,
5
5
  envioApiToken: option<string>,
6
6
  ecosystem: ecosystem,
7
- hyperSyncNetworks: array<int>,
8
- rpcNetworks: array<int>,
7
+ hyperSyncNetworks: array<ChainId.t>,
8
+ rpcNetworks: array<ChainId.t>,
9
9
  }
10
10
 
11
11
  let bodySchema = S.object(s => {
12
12
  envioVersion: s.field("envioVersion", S.string),
13
13
  envioApiToken: s.field("envioApiToken", S.option(S.string)),
14
14
  ecosystem: s.field("ecosystem", S.enum([Evm, Fuel, Svm])),
15
- hyperSyncNetworks: s.field("hyperSyncNetworks", S.array(S.int)),
16
- rpcNetworks: s.field("rpcNetworks", S.array(S.int)),
15
+ hyperSyncNetworks: s.field("hyperSyncNetworks", S.array(ChainId.schema)),
16
+ rpcNetworks: s.field("rpcNetworks", S.array(ChainId.schema)),
17
17
  })
18
18
 
19
19
  let makeBody = (~envioVersion, ~envioApiToken, ~config: Config.t) => {
@@ -28,6 +28,7 @@ module InitApi = {
28
28
  | Config.EvmSourceConfig({hypersync: None}) => false
29
29
  | Config.FuelSourceConfig(_) => true // Fuel always uses HyperFuel
30
30
  | Config.SvmSourceConfig(_) => false
31
+ | Config.SimulateSourceConfig(_) => false
31
32
  | Config.CustomSources(sources) => sources->Array.some(s => s.poweredByHyperSync)
32
33
  }
33
34
  switch usesHyperSync {
@@ -4,6 +4,7 @@ import * as Env from "../../Env.res.mjs";
4
4
  import * as Rest from "../../vendored/Rest.res.mjs";
5
5
  import * as Utils from "../../Utils.res.mjs";
6
6
  import * as React from "react";
7
+ import * as ChainId from "../../ChainId.res.mjs";
7
8
  import * as Logging from "../../Logging.res.mjs";
8
9
  import * as ChainMap from "../../ChainMap.res.mjs";
9
10
  import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
@@ -17,8 +18,8 @@ let bodySchema = S$RescriptSchema.object(s => ({
17
18
  "fuel",
18
19
  "svm"
19
20
  ])),
20
- hyperSyncNetworks: s.f("hyperSyncNetworks", S$RescriptSchema.array(S$RescriptSchema.int)),
21
- rpcNetworks: s.f("rpcNetworks", S$RescriptSchema.array(S$RescriptSchema.int))
21
+ hyperSyncNetworks: s.f("hyperSyncNetworks", S$RescriptSchema.array(ChainId.schema)),
22
+ rpcNetworks: s.f("rpcNetworks", S$RescriptSchema.array(ChainId.schema))
22
23
  }));
23
24
 
24
25
  function makeBody(envioVersion, envioApiToken, config) {
@@ -35,6 +36,7 @@ function makeBody(envioVersion, envioApiToken, config) {
35
36
  usesHyperSync = true;
36
37
  break;
37
38
  case "SvmSourceConfig" :
39
+ case "SimulateSourceConfig" :
38
40
  usesHyperSync = false;
39
41
  break;
40
42
  case "CustomSources" :