envio 3.1.2 → 3.2.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 (134) hide show
  1. package/evm.schema.json +83 -11
  2. package/fuel.schema.json +83 -11
  3. package/index.d.ts +184 -3
  4. package/package.json +6 -6
  5. package/src/Batch.res +2 -2
  6. package/src/ChainFetcher.res +27 -3
  7. package/src/ChainFetcher.res.mjs +17 -3
  8. package/src/ChainManager.res +163 -0
  9. package/src/ChainManager.res.mjs +136 -0
  10. package/src/Config.res +213 -30
  11. package/src/Config.res.mjs +102 -41
  12. package/src/Core.res +16 -10
  13. package/src/Ecosystem.res +0 -3
  14. package/src/Env.res +2 -2
  15. package/src/Env.res.mjs +2 -2
  16. package/src/Envio.res +101 -2
  17. package/src/Envio.res.mjs +2 -3
  18. package/src/EventConfigBuilder.res +52 -0
  19. package/src/EventConfigBuilder.res.mjs +32 -0
  20. package/src/EventUtils.res +2 -2
  21. package/src/FetchState.res +23 -14
  22. package/src/FetchState.res.mjs +21 -15
  23. package/src/GlobalState.res +219 -363
  24. package/src/GlobalState.res.mjs +314 -491
  25. package/src/GlobalStateManager.res +49 -59
  26. package/src/GlobalStateManager.res.mjs +5 -4
  27. package/src/GlobalStateManager.resi +1 -1
  28. package/src/HandlerLoader.res +12 -1
  29. package/src/HandlerLoader.res.mjs +6 -1
  30. package/src/HandlerRegister.res +9 -9
  31. package/src/HandlerRegister.res.mjs +9 -9
  32. package/src/Hasura.res +102 -32
  33. package/src/Hasura.res.mjs +88 -34
  34. package/src/InMemoryStore.res +10 -1
  35. package/src/InMemoryStore.res.mjs +4 -1
  36. package/src/InMemoryTable.res +83 -136
  37. package/src/InMemoryTable.res.mjs +57 -86
  38. package/src/Internal.res +54 -5
  39. package/src/Internal.res.mjs +2 -8
  40. package/src/LazyLoader.res +2 -2
  41. package/src/LazyLoader.res.mjs +3 -3
  42. package/src/LoadLayer.res +47 -60
  43. package/src/LoadLayer.res.mjs +28 -50
  44. package/src/LoadLayer.resi +2 -5
  45. package/src/LogSelection.res +4 -4
  46. package/src/LogSelection.res.mjs +5 -7
  47. package/src/Logging.res +1 -1
  48. package/src/Main.res +61 -2
  49. package/src/Main.res.mjs +37 -1
  50. package/src/Persistence.res +3 -16
  51. package/src/PgStorage.res +125 -114
  52. package/src/PgStorage.res.mjs +112 -95
  53. package/src/Ports.res +5 -0
  54. package/src/Ports.res.mjs +9 -0
  55. package/src/Prometheus.res +3 -3
  56. package/src/Prometheus.res.mjs +4 -4
  57. package/src/ReorgDetection.res +4 -4
  58. package/src/ReorgDetection.res.mjs +4 -5
  59. package/src/SafeCheckpointTracking.res +16 -16
  60. package/src/SafeCheckpointTracking.res.mjs +2 -2
  61. package/src/SimulateItems.res +10 -14
  62. package/src/SimulateItems.res.mjs +5 -2
  63. package/src/Sink.res +1 -1
  64. package/src/Sink.res.mjs +1 -2
  65. package/src/SvmTypes.res +9 -0
  66. package/src/SvmTypes.res.mjs +14 -0
  67. package/src/TestIndexer.res +17 -57
  68. package/src/TestIndexer.res.mjs +14 -48
  69. package/src/TestIndexerProxyStorage.res +23 -23
  70. package/src/TestIndexerProxyStorage.res.mjs +12 -15
  71. package/src/Throttler.res +2 -2
  72. package/src/Time.res +2 -2
  73. package/src/Time.res.mjs +2 -2
  74. package/src/UserContext.res +19 -118
  75. package/src/UserContext.res.mjs +10 -66
  76. package/src/Utils.res +15 -15
  77. package/src/Utils.res.mjs +7 -8
  78. package/src/adapters/MarkBatchProcessedAdapter.res +5 -0
  79. package/src/adapters/MarkBatchProcessedAdapter.res.mjs +14 -0
  80. package/src/bindings/BigDecimal.res +1 -1
  81. package/src/bindings/BigDecimal.res.mjs +2 -2
  82. package/src/bindings/ClickHouse.res +8 -6
  83. package/src/bindings/ClickHouse.res.mjs +5 -5
  84. package/src/bindings/Hrtime.res +1 -1
  85. package/src/bindings/Pino.res +2 -2
  86. package/src/bindings/Pino.res.mjs +3 -4
  87. package/src/db/EntityFilter.res +410 -0
  88. package/src/db/EntityFilter.res.mjs +424 -0
  89. package/src/db/EntityHistory.res +1 -1
  90. package/src/db/EntityHistory.res.mjs +1 -1
  91. package/src/db/InternalTable.res +10 -10
  92. package/src/db/InternalTable.res.mjs +41 -45
  93. package/src/db/Schema.res +2 -2
  94. package/src/db/Schema.res.mjs +3 -3
  95. package/src/db/Table.res +106 -22
  96. package/src/db/Table.res.mjs +84 -35
  97. package/src/sources/EventRouter.res +67 -2
  98. package/src/sources/EventRouter.res.mjs +45 -3
  99. package/src/sources/Evm.res +0 -7
  100. package/src/sources/Evm.res.mjs +0 -15
  101. package/src/sources/EvmChain.res +1 -1
  102. package/src/sources/EvmChain.res.mjs +1 -2
  103. package/src/sources/EvmRpcClient.res +42 -0
  104. package/src/sources/EvmRpcClient.res.mjs +64 -0
  105. package/src/sources/Fuel.res +0 -7
  106. package/src/sources/Fuel.res.mjs +0 -15
  107. package/src/sources/HyperFuelSource.res +5 -4
  108. package/src/sources/HyperFuelSource.res.mjs +2 -2
  109. package/src/sources/HyperSyncClient.res +9 -5
  110. package/src/sources/HyperSyncClient.res.mjs +2 -2
  111. package/src/sources/HyperSyncHeightStream.res +2 -2
  112. package/src/sources/HyperSyncHeightStream.res.mjs +2 -2
  113. package/src/sources/HyperSyncSource.res +10 -9
  114. package/src/sources/HyperSyncSource.res.mjs +4 -4
  115. package/src/sources/Rpc.res +1 -5
  116. package/src/sources/Rpc.res.mjs +1 -9
  117. package/src/sources/RpcSource.res +57 -21
  118. package/src/sources/RpcSource.res.mjs +47 -20
  119. package/src/sources/RpcWebSocketHeightStream.res +1 -1
  120. package/src/sources/SourceManager.res +3 -2
  121. package/src/sources/SourceManager.res.mjs +1 -1
  122. package/src/sources/Svm.res +3 -10
  123. package/src/sources/Svm.res.mjs +4 -18
  124. package/src/sources/SvmHyperSyncClient.res +265 -0
  125. package/src/sources/SvmHyperSyncClient.res.mjs +28 -0
  126. package/src/sources/SvmHyperSyncSource.res +638 -0
  127. package/src/sources/SvmHyperSyncSource.res.mjs +557 -0
  128. package/src/tui/Tui.res +9 -2
  129. package/src/tui/Tui.res.mjs +18 -3
  130. package/src/tui/components/BufferedProgressBar.res +2 -2
  131. package/src/tui/components/TuiData.res +3 -0
  132. package/svm.schema.json +523 -14
  133. package/src/TableIndices.res +0 -115
  134. package/src/TableIndices.res.mjs +0 -144
@@ -2,6 +2,12 @@ open Source
2
2
 
3
3
  exception QueryTimout(string)
4
4
 
5
+ // eth_getTransactionByHash/eth_getTransactionReceipt returning null is usually
6
+ // transient: a load-balanced provider can route the lookup to a node that
7
+ // hasn't caught up with the one that served eth_getLogs. Must stay retryable,
8
+ // unlike other field-selection failures which disable the source.
9
+ exception TransactionDataNotFound({message: string})
10
+
5
11
  // Minimal block data needed for infrastructure (reorg guard, timestamps, etc.)
6
12
  type blockInfo = {
7
13
  number: int,
@@ -14,7 +20,7 @@ let getKnownRawBlock = async (~client, ~blockNumber) =>
14
20
  switch await Rpc.getRawBlock(~client, ~blockNumber) {
15
21
  | Some(json) => json
16
22
  | None =>
17
- JsError.throwWithMessage(`RPC returned null for blockNumber ${blockNumber->Belt.Int.toString}`)
23
+ JsError.throwWithMessage(`RPC returned null for blockNumber ${blockNumber->Int.toString}`)
18
24
  }
19
25
 
20
26
  // Extract infrastructure fields (number, timestamp, hash) from raw block JSON
@@ -56,7 +62,7 @@ let getKnownRawBlockWithBackoff = async (
56
62
  | exception err =>
57
63
  Logging.warn({
58
64
  "err": err->Utils.prettifyExn,
59
- "msg": `Issue while running fetching batch of events from the RPC. Will wait ${currentBackoff.contents->Belt.Int.toString}ms and try again.`,
65
+ "msg": `Issue while running fetching batch of events from the RPC. Will wait ${currentBackoff.contents->Int.toString}ms and try again.`,
60
66
  "source": sourceName,
61
67
  "chainId": chain->ChainMap.Chain.toChainId,
62
68
  "type": "EXPONENTIAL_BACKOFF",
@@ -241,9 +247,7 @@ let getNextPage = (
241
247
  let queryTimoutPromise =
242
248
  Time.resolvePromiseAfterDelay(~delayMilliseconds=sc.queryTimeoutMillis)->Promise.then(() =>
243
249
  Promise.reject(
244
- QueryTimout(
245
- `Query took longer than ${Belt.Int.toString(sc.queryTimeoutMillis / 1000)} seconds`,
246
- ),
250
+ QueryTimout(`Query took longer than ${Int.toString(sc.queryTimeoutMillis / 1000)} seconds`),
247
251
  )
248
252
  )
249
253
 
@@ -287,7 +291,7 @@ let getNextPage = (
287
291
  | None =>
288
292
  let executedBlockInterval = toBlock - fromBlock + 1
289
293
  let nextBlockIntervalTry =
290
- (executedBlockInterval->Belt.Int.toFloat *. sc.backoffMultiplicative)->Belt.Int.fromFloat
294
+ (executedBlockInterval->Int.toFloat *. sc.backoffMultiplicative)->Float.toInt
291
295
  mutSuggestedBlockIntervals->Dict.set(partitionId, nextBlockIntervalTry)
292
296
  throw(
293
297
  Source.GetItemsError(
@@ -320,7 +324,7 @@ let getSelectionConfig = (selection: FetchState.selection, ~chain) => {
320
324
 
321
325
  selection.eventConfigs
322
326
  ->(Utils.magic: array<Internal.eventConfig> => array<Internal.evmEventConfig>)
323
- ->Belt.Array.forEach(({getEventFiltersOrThrow}) => {
327
+ ->Array.forEach(({getEventFiltersOrThrow}) => {
324
328
  switch getEventFiltersOrThrow(chain) {
325
329
  | Static(s) => staticTopicSelections->Array.pushMany(s)->ignore
326
330
  | Dynamic(fn) => dynamicEventFilters->Array.push(fn)->ignore
@@ -527,6 +531,12 @@ let makeThrowingGetEventBlock = (
527
531
  }
528
532
  }
529
533
 
534
+ // `number`, `timestamp` and `hash` are always part of the selected block
535
+ // fields, so they can be read from the assembled block at item construction.
536
+ @get external getBlockNumber: Internal.eventBlock => int = "number"
537
+ @get external getBlockTimestamp: Internal.eventBlock => int = "timestamp"
538
+ @get external getBlockHash: Internal.eventBlock => string = "hash"
539
+
530
540
  // Field source classification for RPC calls
531
541
  type fieldSource = TransactionOnly | ReceiptOnly | Both
532
542
 
@@ -863,7 +873,7 @@ let make = (
863
873
  let urlHost = switch Utils.Url.getHostFromUrl(url) {
864
874
  | None =>
865
875
  JsError.throwWithMessage(
866
- `The RPC url for chain ${chainId->Belt.Int.toString} is in incorrect format. The RPC url needs to start with either http:// or https://`,
876
+ `The RPC url for chain ${chainId->Int.toString} is in incorrect format. The RPC url needs to start with either http:// or https://`,
867
877
  )
868
878
  | Some(host) => host
869
879
  }
@@ -874,6 +884,7 @@ let make = (
874
884
  let mutSuggestedBlockIntervals = Dict.make()
875
885
 
876
886
  let client = Rpc.makeClient(url)
887
+ let rpcClient = EvmRpcClient.make(~url)
877
888
 
878
889
  let makeTransactionLoader = () =>
879
890
  LazyLoader.make(
@@ -889,7 +900,7 @@ let make = (
889
900
  Logging.error({
890
901
  "err": exn->Utils.prettifyExn,
891
902
  "msg": `Top level promise timeout reached. Please review other errors or warnings in the code. This function will retry in ${(am._retryDelayMillis / 1000)
892
- ->Belt.Int.toString} seconds. It is highly likely that your indexer isn't syncing on one or more chains currently. Also take a look at the "suggestedFix" in the metadata of this command`,
903
+ ->Int.toString} seconds. It is highly likely that your indexer isn't syncing on one or more chains currently. Also take a look at the "suggestedFix" in the metadata of this command`,
893
904
  "source": name,
894
905
  "chainId": chain->ChainMap.Chain.toChainId,
895
906
  "metadata": {
@@ -917,7 +928,7 @@ let make = (
917
928
  Logging.error({
918
929
  "err": exn->Utils.prettifyExn,
919
930
  "msg": `Top level promise timeout reached. Please review other errors or warnings in the code. This function will retry in ${(am._retryDelayMillis / 1000)
920
- ->Belt.Int.toString} seconds. It is highly likely that your indexer isn't syncing on one or more chains currently. Also take a look at the "suggestedFix" in the metadata of this command`,
931
+ ->Int.toString} seconds. It is highly likely that your indexer isn't syncing on one or more chains currently. Also take a look at the "suggestedFix" in the metadata of this command`,
921
932
  "source": name,
922
933
  "chainId": chain->ChainMap.Chain.toChainId,
923
934
  "metadata": {
@@ -944,7 +955,7 @@ let make = (
944
955
  Logging.error({
945
956
  "err": exn->Utils.prettifyExn,
946
957
  "msg": `Top level promise timeout reached. Please review other errors or warnings in the code. This function will retry in ${(am._retryDelayMillis / 1000)
947
- ->Belt.Int.toString} seconds. It is highly likely that your indexer isn't syncing on one or more chains currently. Also take a look at the "suggestedFix" in the metadata of this command`,
958
+ ->Int.toString} seconds. It is highly likely that your indexer isn't syncing on one or more chains currently. Also take a look at the "suggestedFix" in the metadata of this command`,
948
959
  "source": name,
949
960
  "chainId": chain->ChainMap.Chain.toChainId,
950
961
  "metadata": {
@@ -969,14 +980,21 @@ let make = (
969
980
  ~getTransactionJson=async transactionHash => {
970
981
  switch await transactionLoader.contents->LazyLoader.get(transactionHash) {
971
982
  | Some(json) => json
972
- | None => JsError.throwWithMessage(`Transaction not found for hash: ${transactionHash}`)
983
+ | None =>
984
+ throw(
985
+ TransactionDataNotFound({message: `Transaction not found for hash: ${transactionHash}`}),
986
+ )
973
987
  }
974
988
  },
975
989
  ~getReceiptJson=async transactionHash => {
976
990
  switch await receiptLoader.contents->LazyLoader.get(transactionHash) {
977
991
  | Some(json) => json
978
992
  | None =>
979
- JsError.throwWithMessage(`Transaction receipt not found for hash: ${transactionHash}`)
993
+ throw(
994
+ TransactionDataNotFound({
995
+ message: `Transaction receipt not found for hash: ${transactionHash}`,
996
+ }),
997
+ )
980
998
  }
981
999
  },
982
1000
  ~lowercaseAddresses,
@@ -1020,7 +1038,7 @@ let make = (
1020
1038
  ~knownHeight,
1021
1039
  ~partitionId,
1022
1040
  ~selection: FetchState.selection,
1023
- ~retry as _,
1041
+ ~retry,
1024
1042
  ~logger as _,
1025
1043
  ) => {
1026
1044
  let startFetchingBatchTimeRef = Hrtime.makeTimer()
@@ -1032,7 +1050,7 @@ let make = (
1032
1050
  | None =>
1033
1051
  mutSuggestedBlockIntervals
1034
1052
  ->Utils.Dict.dangerouslyGetNonOption(partitionId)
1035
- ->Belt.Option.getWithDefault(syncConfig.initialBlockInterval)
1053
+ ->Option.getOr(syncConfig.initialBlockInterval)
1036
1054
  }
1037
1055
 
1038
1056
  // Always have a toBlock for an RPC worker
@@ -1093,7 +1111,7 @@ let make = (
1093
1111
  }
1094
1112
 
1095
1113
  // Convert RPC logs to HyperSync events
1096
- let hyperSyncEvents = logs->Belt.Array.map(convertLogToHyperSyncEvent)
1114
+ let hyperSyncEvents = logs->Array.map(convertLogToHyperSyncEvent)
1097
1115
 
1098
1116
  // Decode using HyperSyncClient decoder
1099
1117
  let parsedEvents = try await getHscDecoder().decodeLogs(hyperSyncEvents) catch {
@@ -1145,6 +1163,23 @@ let make = (
1145
1163
  ~selectedTransactionFields=eventConfig.selectedTransactionFields,
1146
1164
  ),
1147
1165
  )) catch {
1166
+ | TransactionDataNotFound({message}) =>
1167
+ let backoffMillis = switch retry {
1168
+ | 0 => 100
1169
+ | _ => 500 * retry
1170
+ }
1171
+ throw(
1172
+ Source.GetItemsError(
1173
+ FailedGettingItems({
1174
+ exn: %raw(`null`),
1175
+ attemptedToBlock: toBlock,
1176
+ retry: WithBackoff({
1177
+ message: `${message}. The RPC provider might be load-balanced between nodes that drift independently slightly from the head. Indexing should continue correctly after retrying the query in ${backoffMillis->Int.toString}ms.`,
1178
+ backoffMillis,
1179
+ }),
1180
+ }),
1181
+ ),
1182
+ )
1148
1183
  | exn =>
1149
1184
  throw(
1150
1185
  Source.GetItemsError(
@@ -1160,8 +1195,9 @@ let make = (
1160
1195
 
1161
1196
  Internal.Event({
1162
1197
  eventConfig: (eventConfig :> Internal.eventConfig),
1163
- timestamp: block->Evm.getTimestamp,
1164
- blockNumber: block->Evm.getNumber,
1198
+ timestamp: block->getBlockTimestamp,
1199
+ blockNumber: block->getBlockNumber,
1200
+ blockHash: block->getBlockHash,
1165
1201
  chain,
1166
1202
  logIndex: log.logIndex,
1167
1203
  event: {
@@ -1204,7 +1240,7 @@ let make = (
1204
1240
  | Some(b) => pushBlockInfo(b)
1205
1241
  | None => ()
1206
1242
  }
1207
- logs->Belt.Array.forEach(log =>
1243
+ logs->Array.forEach(log =>
1208
1244
  blockHashes
1209
1245
  ->Array.push({ReorgDetection.blockNumber: log.blockNumber, blockHash: log.blockHash})
1210
1246
  ->ignore
@@ -1254,7 +1290,7 @@ let make = (
1254
1290
  }
1255
1291
 
1256
1292
  let createHeightSubscription =
1257
- ws->Belt.Option.map(wsUrl =>
1293
+ ws->Option.map(wsUrl =>
1258
1294
  (~onHeight) => RpcWebSocketHeightStream.subscribe(~wsUrl, ~chainId, ~onHeight)
1259
1295
  )
1260
1296
 
@@ -1269,7 +1305,7 @@ let make = (
1269
1305
  getHeightOrThrow: async () => {
1270
1306
  let timerRef = Hrtime.makeTimer()
1271
1307
  let height = try {
1272
- await Rpc.GetBlockHeight.route->Rest.fetch((), ~client)
1308
+ await rpcClient.getHeight()
1273
1309
  } catch {
1274
1310
  | exn =>
1275
1311
  let seconds = timerRef->Hrtime.timeSince->Hrtime.toSecondsFloat
@@ -9,13 +9,12 @@ import * as Source from "./Source.res.mjs";
9
9
  import * as Address from "../Address.res.mjs";
10
10
  import * as Logging from "../Logging.res.mjs";
11
11
  import * as Internal from "../Internal.res.mjs";
12
- import * as Belt_Array from "@rescript/runtime/lib/es6/Belt_Array.js";
13
12
  import * as FetchState from "../FetchState.res.mjs";
14
13
  import * as LazyLoader from "../LazyLoader.res.mjs";
15
14
  import * as Prometheus from "../Prometheus.res.mjs";
16
15
  import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
17
- import * as Belt_Option from "@rescript/runtime/lib/es6/Belt_Option.js";
18
16
  import * as EventRouter from "./EventRouter.res.mjs";
17
+ import * as EvmRpcClient from "./EvmRpcClient.res.mjs";
19
18
  import * as LogSelection from "../LogSelection.res.mjs";
20
19
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
21
20
  import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
@@ -30,12 +29,14 @@ import * as RpcWebSocketHeightStream from "./RpcWebSocketHeightStream.res.mjs";
30
29
 
31
30
  let QueryTimout = /* @__PURE__ */Primitive_exceptions.create("RpcSource.QueryTimout");
32
31
 
32
+ let TransactionDataNotFound = /* @__PURE__ */Primitive_exceptions.create("RpcSource.TransactionDataNotFound");
33
+
33
34
  async function getKnownRawBlock(client, blockNumber) {
34
35
  let json = await Rpc.getRawBlock(client, blockNumber);
35
36
  if (json !== undefined) {
36
37
  return json;
37
38
  } else {
38
- return Stdlib_JsError.throwWithMessage(`RPC returned null for blockNumber ` + String(blockNumber));
39
+ return Stdlib_JsError.throwWithMessage(`RPC returned null for blockNumber ` + blockNumber.toString());
39
40
  }
40
41
  }
41
42
 
@@ -62,7 +63,7 @@ async function getKnownRawBlockWithBackoff(client, sourceName, chain, blockNumbe
62
63
  let err = Primitive_exceptions.internalToException(raw_err);
63
64
  Logging.warn({
64
65
  err: Utils.prettifyExn(err),
65
- msg: `Issue while running fetching batch of events from the RPC. Will wait ` + String(currentBackoff) + `ms and try again.`,
66
+ msg: `Issue while running fetching batch of events from the RPC. Will wait ` + currentBackoff.toString() + `ms and try again.`,
66
67
  source: sourceName,
67
68
  chainId: chain,
68
69
  type: "EXPONENTIAL_BACKOFF"
@@ -223,7 +224,7 @@ let maxSuggestedBlockIntervalKey = "max";
223
224
  function getNextPage(fromBlock, toBlock, addresses, topicQuery, loadBlock, sc, client, mutSuggestedBlockIntervals, partitionId, sourceName, chainId) {
224
225
  let queryTimoutPromise = Time.resolvePromiseAfterDelay(sc.queryTimeoutMillis).then(() => Promise.reject({
225
226
  RE_EXN_ID: QueryTimout,
226
- _1: `Query took longer than ` + String(sc.queryTimeoutMillis / 1000 | 0) + ` seconds`
227
+ _1: `Query took longer than ` + (sc.queryTimeoutMillis / 1000 | 0).toString() + ` seconds`
227
228
  }));
228
229
  let latestFetchedBlockPromise = loadBlock(toBlock);
229
230
  Prometheus.SourceRequestCount.increment(sourceName, chainId, "eth_getLogs");
@@ -281,7 +282,7 @@ function getNextPage(fromBlock, toBlock, addresses, topicQuery, loadBlock, sc, c
281
282
  function getSelectionConfig(selection, chain) {
282
283
  let staticTopicSelections = [];
283
284
  let dynamicEventFilters = [];
284
- Belt_Array.forEach(selection.eventConfigs, param => {
285
+ selection.eventConfigs.forEach(param => {
285
286
  let s = param.getEventFiltersOrThrow(chain);
286
287
  if (s.TAG === "Static") {
287
288
  staticTopicSelections.push(...s._0);
@@ -924,17 +925,18 @@ function make(param) {
924
925
  let url = param.url;
925
926
  let syncConfig = param.syncConfig;
926
927
  let host = Utils.Url.getHostFromUrl(url);
927
- let urlHost = host !== undefined ? host : Stdlib_JsError.throwWithMessage(`The RPC url for chain ` + String(chain) + ` is in incorrect format. The RPC url needs to start with either http:// or https://`);
928
+ 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://`);
928
929
  let name = `RPC (` + urlHost + `)`;
929
930
  let getSelectionConfig = memoGetSelectionConfig(chain);
930
931
  let mutSuggestedBlockIntervals = {};
931
932
  let client = Rpc.makeClient(url);
933
+ let rpcClient = EvmRpcClient.make(url, undefined);
932
934
  let makeTransactionLoader = () => LazyLoader.make(transactionHash => {
933
935
  Prometheus.SourceRequestCount.increment(name, chain, "eth_getTransactionByHash");
934
936
  return Rest.fetch(Rpc.GetTransactionByHash.rawRoute, transactionHash, client);
935
937
  }, (am, exn) => Logging.error({
936
938
  err: Utils.prettifyExn(exn),
937
- msg: `Top level promise timeout reached. Please review other errors or warnings in the code. This function will retry in ` + String(am._retryDelayMillis / 1000 | 0) + ` seconds. It is highly likely that your indexer isn't syncing on one or more chains currently. Also take a look at the "suggestedFix" in the metadata of this command`,
939
+ msg: `Top level promise timeout reached. Please review other errors or warnings in the code. This function will retry in ` + (am._retryDelayMillis / 1000 | 0).toString() + ` seconds. It is highly likely that your indexer isn't syncing on one or more chains currently. Also take a look at the "suggestedFix" in the metadata of this command`,
938
940
  source: name,
939
941
  chainId: chain,
940
942
  metadata: {
@@ -944,7 +946,7 @@ function make(param) {
944
946
  }), undefined, undefined, undefined, undefined);
945
947
  let makeBlockLoader = () => LazyLoader.make(blockNumber => getKnownRawBlockWithBackoff(client, name, chain, blockNumber, 1000), (am, exn) => Logging.error({
946
948
  err: Utils.prettifyExn(exn),
947
- msg: `Top level promise timeout reached. Please review other errors or warnings in the code. This function will retry in ` + String(am._retryDelayMillis / 1000 | 0) + ` seconds. It is highly likely that your indexer isn't syncing on one or more chains currently. Also take a look at the "suggestedFix" in the metadata of this command`,
949
+ msg: `Top level promise timeout reached. Please review other errors or warnings in the code. This function will retry in ` + (am._retryDelayMillis / 1000 | 0).toString() + ` seconds. It is highly likely that your indexer isn't syncing on one or more chains currently. Also take a look at the "suggestedFix" in the metadata of this command`,
948
950
  source: name,
949
951
  chainId: chain,
950
952
  metadata: {
@@ -957,7 +959,7 @@ function make(param) {
957
959
  return Rest.fetch(Rpc.GetTransactionReceipt.rawRoute, transactionHash, client);
958
960
  }, (am, exn) => Logging.error({
959
961
  err: Utils.prettifyExn(exn),
960
- msg: `Top level promise timeout reached. Please review other errors or warnings in the code. This function will retry in ` + String(am._retryDelayMillis / 1000 | 0) + ` seconds. It is highly likely that your indexer isn't syncing on one or more chains currently. Also take a look at the "suggestedFix" in the metadata of this command`,
962
+ msg: `Top level promise timeout reached. Please review other errors or warnings in the code. This function will retry in ` + (am._retryDelayMillis / 1000 | 0).toString() + ` seconds. It is highly likely that your indexer isn't syncing on one or more chains currently. Also take a look at the "suggestedFix" in the metadata of this command`,
961
963
  source: name,
962
964
  chainId: chain,
963
965
  metadata: {
@@ -979,16 +981,22 @@ function make(param) {
979
981
  let json = await LazyLoader.get(transactionLoader.contents, transactionHash);
980
982
  if (json !== undefined) {
981
983
  return json;
982
- } else {
983
- return Stdlib_JsError.throwWithMessage(`Transaction not found for hash: ` + transactionHash);
984
984
  }
985
+ throw {
986
+ RE_EXN_ID: TransactionDataNotFound,
987
+ message: `Transaction not found for hash: ` + transactionHash,
988
+ Error: new Error()
989
+ };
985
990
  }, async transactionHash => {
986
991
  let json = await LazyLoader.get(receiptLoader.contents, transactionHash);
987
992
  if (json !== undefined) {
988
993
  return json;
989
- } else {
990
- return Stdlib_JsError.throwWithMessage(`Transaction receipt not found for hash: ` + transactionHash);
991
994
  }
995
+ throw {
996
+ RE_EXN_ID: TransactionDataNotFound,
997
+ message: `Transaction receipt not found for hash: ` + transactionHash,
998
+ Error: new Error()
999
+ };
992
1000
  }, lowercaseAddresses);
993
1001
  let convertLogToHyperSyncEvent = log => {
994
1002
  let hyperSyncLog_removed = log.removed;
@@ -1027,10 +1035,10 @@ function make(param) {
1027
1035
  hscDecoder.contents = decoder$1;
1028
1036
  return decoder$1;
1029
1037
  };
1030
- let getItemsOrThrow = async (fromBlock, toBlock, addressesByContractName, indexingAddresses, knownHeight, partitionId, selection, param, param$1) => {
1038
+ let getItemsOrThrow = async (fromBlock, toBlock, addressesByContractName, indexingAddresses, knownHeight, partitionId, selection, retry, param) => {
1031
1039
  let startFetchingBatchTimeRef = Hrtime.makeTimer();
1032
1040
  let maxSuggestedBlockInterval = mutSuggestedBlockIntervals[maxSuggestedBlockIntervalKey];
1033
- let suggestedBlockInterval = maxSuggestedBlockInterval !== undefined ? maxSuggestedBlockInterval : Belt_Option.getWithDefault(mutSuggestedBlockIntervals[partitionId], syncConfig.initialBlockInterval);
1041
+ let suggestedBlockInterval = maxSuggestedBlockInterval !== undefined ? maxSuggestedBlockInterval : Stdlib_Option.getOr(mutSuggestedBlockIntervals[partitionId], syncConfig.initialBlockInterval);
1034
1042
  let toBlock$1 = toBlock !== undefined ? Primitive_int.min(toBlock, knownHeight) : knownHeight;
1035
1043
  let suggestedToBlock = Primitive_int.max(Primitive_int.min((fromBlock + suggestedBlockInterval | 0) - 1 | 0, toBlock$1), fromBlock);
1036
1044
  let firstBlockParentPromise = fromBlock > 0 ? LazyLoader.get(blockLoader.contents, fromBlock - 1 | 0).then(json => parseBlockInfo(json)) : Promise.resolve(undefined);
@@ -1043,7 +1051,7 @@ function make(param) {
1043
1051
  if (executedBlockInterval >= suggestedBlockInterval && !(maxSuggestedBlockIntervalKey in mutSuggestedBlockIntervals)) {
1044
1052
  mutSuggestedBlockIntervals[partitionId] = Primitive_int.min(executedBlockInterval + syncConfig.accelerationAdditive | 0, syncConfig.intervalCeiling);
1045
1053
  }
1046
- let hyperSyncEvents = Belt_Array.map(logs, convertLogToHyperSyncEvent);
1054
+ let hyperSyncEvents = logs.map(convertLogToHyperSyncEvent);
1047
1055
  let parsedEvents;
1048
1056
  try {
1049
1057
  parsedEvents = await getHscDecoder().decodeLogs(hyperSyncEvents);
@@ -1085,6 +1093,23 @@ function make(param) {
1085
1093
  ]);
1086
1094
  } catch (raw_exn) {
1087
1095
  let exn = Primitive_exceptions.internalToException(raw_exn);
1096
+ if (exn.RE_EXN_ID === TransactionDataNotFound) {
1097
+ let backoffMillis = retry !== 0 ? 500 * retry | 0 : 100;
1098
+ throw {
1099
+ RE_EXN_ID: Source.GetItemsError,
1100
+ _1: {
1101
+ TAG: "FailedGettingItems",
1102
+ exn: null,
1103
+ attemptedToBlock: toBlock$1,
1104
+ retry: {
1105
+ TAG: "WithBackoff",
1106
+ message: exn.message + `. The RPC provider might be load-balanced between nodes that drift independently slightly from the head. Indexing should continue correctly after retrying the query in ` + backoffMillis.toString() + `ms.`,
1107
+ backoffMillis: backoffMillis
1108
+ }
1109
+ },
1110
+ Error: new Error()
1111
+ };
1112
+ }
1088
1113
  throw {
1089
1114
  RE_EXN_ID: Source.GetItemsError,
1090
1115
  _1: {
@@ -1104,6 +1129,7 @@ function make(param) {
1104
1129
  timestamp: block.timestamp,
1105
1130
  chain: chain,
1106
1131
  blockNumber: block.number,
1132
+ blockHash: block.hash,
1107
1133
  logIndex: log.logIndex,
1108
1134
  event: {
1109
1135
  contractName: eventConfig.contractName,
@@ -1138,7 +1164,7 @@ function make(param) {
1138
1164
  if (optFirstBlockParent !== undefined) {
1139
1165
  pushBlockInfo(optFirstBlockParent);
1140
1166
  }
1141
- Belt_Array.forEach(logs, log => {
1167
+ logs.forEach(log => {
1142
1168
  blockHashes.push({
1143
1169
  blockHash: log.blockHash,
1144
1170
  blockNumber: log.blockNumber
@@ -1175,7 +1201,7 @@ function make(param) {
1175
1201
  TAG: "Error",
1176
1202
  _0: exn
1177
1203
  }));
1178
- let createHeightSubscription = Belt_Option.map(param.ws, wsUrl => (onHeight => RpcWebSocketHeightStream.subscribe(wsUrl, chain, onHeight)));
1204
+ let createHeightSubscription = Stdlib_Option.map(param.ws, wsUrl => (onHeight => RpcWebSocketHeightStream.subscribe(wsUrl, chain, onHeight)));
1179
1205
  return {
1180
1206
  name: name,
1181
1207
  sourceFor: param.sourceFor,
@@ -1187,7 +1213,7 @@ function make(param) {
1187
1213
  let timerRef = Hrtime.makeTimer();
1188
1214
  let height;
1189
1215
  try {
1190
- height = await Rest.fetch(Rpc.GetBlockHeight.route, undefined, client);
1216
+ height = await rpcClient.getHeight();
1191
1217
  } catch (exn) {
1192
1218
  let seconds = Hrtime.toSecondsFloat(Hrtime.timeSince(timerRef));
1193
1219
  Prometheus.SourceRequestCount.increment(name, chain, "eth_blockNumber");
@@ -1207,6 +1233,7 @@ function make(param) {
1207
1233
 
1208
1234
  export {
1209
1235
  QueryTimout,
1236
+ TransactionDataNotFound,
1210
1237
  getKnownRawBlock,
1211
1238
  parseBlockInfo,
1212
1239
  getKnownRawBlockWithBackoff,
@@ -82,7 +82,7 @@ let subscribe = (~wsUrl, ~chainId, ~onHeight: int => unit): (unit => unit) => {
82
82
  let rec scheduleReconnect = () => {
83
83
  if !isUnsubscribed.contents && errorCount.contents < retryCount {
84
84
  let duration =
85
- baseDuration * Math.pow(2.0, ~exp=errorCount.contents->Belt.Int.toFloat)->Belt.Float.toInt
85
+ baseDuration * Math.pow(2.0, ~exp=errorCount.contents->Int.toFloat)->Float.toInt
86
86
  let _ = setTimeout(() => {
87
87
  if !isUnsubscribed.contents {
88
88
  startConnection()
@@ -338,7 +338,7 @@ let getSourceNewHeight = async (
338
338
  // that go quiet together don't all start polling at the same instant.
339
339
  let half = stallTimeout / 2
340
340
  let pollingFallback = Utils.delay(
341
- half + (Math.random() *. half->Belt.Int.toFloat)->Belt.Float.toInt,
341
+ half + (Math.random() *. half->Int.toFloat)->Float.toInt,
342
342
  )->Promise.then(async () => {
343
343
  logger->Logging.childTrace({
344
344
  "msg": "onHeight subscription stale, switching to polling fallback",
@@ -546,10 +546,11 @@ let waitForNewBlock = async (sourceManager: t, ~knownHeight, ~isRealtime, ~reduc
546
546
  })
547
547
  ->Array.concat([
548
548
  Utils.delay(stallTimeout)->Promise.then(() => {
549
- // Build fallback: sources not in mainSources with a valid role, even with recent lastFailedAt
549
+ // Build fallback: non-disabled sources not in mainSources with a valid role, even with recent lastFailedAt
550
550
  let fallbackSources = []
551
551
  sourcesState->Array.forEach(sourceState => {
552
552
  if (
553
+ !sourceState.disabled &&
553
554
  !(mainSources->Array.includes(sourceState)) &&
554
555
  getSourceRole(
555
556
  ~sourceFor=sourceState.source.sourceFor,
@@ -429,7 +429,7 @@ async function waitForNewBlock(sourceManager, knownHeight, isRealtime, reducedPo
429
429
  ]).concat([Utils.delay(stallTimeout).then(() => {
430
430
  let fallbackSources = [];
431
431
  sourcesState.forEach(sourceState => {
432
- if (!mainSources.includes(sourceState) && Stdlib_Option.isSome(getSourceRole(sourceState.source.sourceFor, isRealtime, sourceManager.hasRealtime))) {
432
+ if (!sourceState.disabled && !mainSources.includes(sourceState) && Stdlib_Option.isSome(getSourceRole(sourceState.source.sourceFor, isRealtime, sourceManager.hasRealtime))) {
433
433
  fallbackSources.push(sourceState);
434
434
  return;
435
435
  }
@@ -1,7 +1,3 @@
1
- @get external getNumber: Internal.eventBlock => int = "height"
2
- @get external getTimestamp: Internal.eventBlock => int = "time"
3
- @get external getId: Internal.eventBlock => string = "hash"
4
-
5
1
  let cleanUpRawEventFieldsInPlace: JSON.t => unit = %raw(`fields => {
6
2
  delete fields.hash
7
3
  delete fields.height
@@ -15,9 +11,6 @@ let ecosystem: Ecosystem.t = {
15
11
  blockNumberName: "height",
16
12
  blockTimestampName: "time",
17
13
  blockHashName: "hash",
18
- getNumber,
19
- getTimestamp,
20
- getId,
21
14
  cleanUpRawEventFieldsInPlace,
22
15
  onBlockMethodName: "onSlot",
23
16
  // SVM filter shape: `{slot: {_gte?, _lte?, _every?}}`.
@@ -39,14 +32,14 @@ module GetFinalizedSlot = {
39
32
  )
40
33
  }
41
34
 
42
- let makeRPCSource = (~chain, ~rpc: string): Source.t => {
35
+ let makeRPCSource = (~chain, ~rpc: string, ~sourceFor: Source.sourceFor=Sync): Source.t => {
43
36
  let client = Rest.client(rpc)
44
37
  let chainId = chain->ChainMap.Chain.toChainId
45
38
 
46
39
  let urlHost = switch Utils.Url.getHostFromUrl(rpc) {
47
40
  | None =>
48
41
  JsError.throwWithMessage(
49
- `The RPC url for chain ${chainId->Belt.Int.toString} is in incorrect format. The RPC url needs to start with either http:// or https://`,
42
+ `The RPC url for chain ${chainId->Int.toString} is in incorrect format. The RPC url needs to start with either http:// or https://`,
50
43
  )
51
44
  | Some(host) => host
52
45
  }
@@ -54,7 +47,7 @@ let makeRPCSource = (~chain, ~rpc: string): Source.t => {
54
47
 
55
48
  {
56
49
  name,
57
- sourceFor: Sync,
50
+ sourceFor,
58
51
  chain,
59
52
  poweredByHyperSync: false,
60
53
  pollingInterval: 10_000,
@@ -18,18 +18,6 @@ let ecosystem_blockFields = ["slot"];
18
18
 
19
19
  let ecosystem_transactionFields = [];
20
20
 
21
- function ecosystem_getNumber(prim) {
22
- return prim.height;
23
- }
24
-
25
- function ecosystem_getTimestamp(prim) {
26
- return prim.time;
27
- }
28
-
29
- function ecosystem_getId(prim) {
30
- return prim.hash;
31
- }
32
-
33
21
  let ecosystem_onBlockFilterSchema = S$RescriptSchema.object(s => s.f("slot", S$RescriptSchema.option(S$RescriptSchema.unknown)));
34
22
 
35
23
  let ecosystem_onEventBlockFilterSchema = S$RescriptSchema.object(param => {});
@@ -41,9 +29,6 @@ let ecosystem = {
41
29
  blockNumberName: "height",
42
30
  blockTimestampName: "time",
43
31
  blockHashName: "hash",
44
- getNumber: ecosystem_getNumber,
45
- getTimestamp: ecosystem_getTimestamp,
46
- getId: ecosystem_getId,
47
32
  cleanUpRawEventFieldsInPlace: cleanUpRawEventFieldsInPlace,
48
33
  onBlockMethodName: "onSlot",
49
34
  onBlockFilterSchema: ecosystem_onBlockFilterSchema,
@@ -60,14 +45,15 @@ let GetFinalizedSlot = {
60
45
  route: route
61
46
  };
62
47
 
63
- function makeRPCSource(chain, rpc) {
48
+ function makeRPCSource(chain, rpc, sourceForOpt) {
49
+ let sourceFor = sourceForOpt !== undefined ? sourceForOpt : "Sync";
64
50
  let client = Rest.client(rpc, undefined);
65
51
  let host = Utils.Url.getHostFromUrl(rpc);
66
- let urlHost = host !== undefined ? host : Stdlib_JsError.throwWithMessage(`The RPC url for chain ` + String(chain) + ` is in incorrect format. The RPC url needs to start with either http:// or https://`);
52
+ 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://`);
67
53
  let name = `RPC (` + urlHost + `)`;
68
54
  return {
69
55
  name: name,
70
- sourceFor: "Sync",
56
+ sourceFor: sourceFor,
71
57
  chain: chain,
72
58
  poweredByHyperSync: false,
73
59
  pollingInterval: 10000,