envio 3.3.0-alpha.1 → 3.3.0-alpha.10

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 (136) hide show
  1. package/evm.schema.json +10 -0
  2. package/index.d.ts +45 -25
  3. package/package.json +7 -7
  4. package/src/Address.res +5 -2
  5. package/src/Address.res.mjs +3 -1
  6. package/src/Api.res +1 -1
  7. package/src/Api.res.mjs +1 -1
  8. package/src/Batch.res +0 -6
  9. package/src/Batch.res.mjs +1 -12
  10. package/src/BatchProcessing.res +7 -12
  11. package/src/BatchProcessing.res.mjs +8 -7
  12. package/src/Bin.res +3 -0
  13. package/src/Bin.res.mjs +4 -0
  14. package/src/ChainFetching.res +43 -9
  15. package/src/ChainFetching.res.mjs +34 -15
  16. package/src/ChainMetadata.res +1 -11
  17. package/src/ChainMetadata.res.mjs +1 -10
  18. package/src/ChainState.res +605 -151
  19. package/src/ChainState.res.mjs +427 -104
  20. package/src/ChainState.resi +72 -4
  21. package/src/Config.res +61 -33
  22. package/src/Config.res.mjs +50 -10
  23. package/src/ContractRegisterContext.res +2 -12
  24. package/src/ContractRegisterContext.res.mjs +3 -5
  25. package/src/Core.res +14 -3
  26. package/src/CrossChainState.res +113 -89
  27. package/src/CrossChainState.res.mjs +64 -60
  28. package/src/CrossChainState.resi +1 -1
  29. package/src/Ecosystem.res +5 -6
  30. package/src/Ecosystem.res.mjs +3 -3
  31. package/src/Envio.res +17 -11
  32. package/src/EnvioGlobal.res +53 -0
  33. package/src/EnvioGlobal.res.mjs +31 -0
  34. package/src/EventConfigBuilder.res +177 -88
  35. package/src/EventConfigBuilder.res.mjs +99 -42
  36. package/src/EventProcessing.res +74 -28
  37. package/src/EventProcessing.res.mjs +55 -29
  38. package/src/ExitOnCaughtUp.res +10 -2
  39. package/src/ExitOnCaughtUp.res.mjs +10 -1
  40. package/src/FetchState.res +544 -356
  41. package/src/FetchState.res.mjs +412 -471
  42. package/src/HandlerLoader.res +8 -104
  43. package/src/HandlerLoader.res.mjs +2 -81
  44. package/src/HandlerRegister.res +518 -144
  45. package/src/HandlerRegister.res.mjs +285 -133
  46. package/src/HandlerRegister.resi +24 -8
  47. package/src/IndexerState.res +10 -3
  48. package/src/IndexerState.res.mjs +11 -4
  49. package/src/IndexerState.resi +2 -1
  50. package/src/IndexingAddresses.res +108 -0
  51. package/src/IndexingAddresses.res.mjs +101 -0
  52. package/src/IndexingAddresses.resi +34 -0
  53. package/src/Internal.res +155 -46
  54. package/src/Internal.res.mjs +30 -2
  55. package/src/LoadLayer.res +5 -5
  56. package/src/LoadLayer.res.mjs +6 -6
  57. package/src/LogSelection.res +102 -165
  58. package/src/LogSelection.res.mjs +101 -116
  59. package/src/Main.res +71 -236
  60. package/src/Main.res.mjs +60 -145
  61. package/src/Metrics.res +74 -0
  62. package/src/Metrics.res.mjs +75 -0
  63. package/src/PgStorage.res +4 -4
  64. package/src/PgStorage.res.mjs +5 -5
  65. package/src/Prometheus.res +10 -63
  66. package/src/Prometheus.res.mjs +124 -171
  67. package/src/PruneStaleHistory.res +2 -2
  68. package/src/PruneStaleHistory.res.mjs +3 -3
  69. package/src/RawEvent.res +7 -2
  70. package/src/RawEvent.res.mjs +4 -4
  71. package/src/Rollback.res +3 -3
  72. package/src/Rollback.res.mjs +4 -4
  73. package/src/RollbackCommit.res +4 -1
  74. package/src/RollbackCommit.res.mjs +3 -2
  75. package/src/SimulateDeadInputTracker.res +85 -0
  76. package/src/SimulateDeadInputTracker.res.mjs +73 -0
  77. package/src/SimulateDeadInputTracker.resi +12 -0
  78. package/src/SimulateItems.res +91 -23
  79. package/src/SimulateItems.res.mjs +55 -32
  80. package/src/TestIndexer.res +54 -40
  81. package/src/TestIndexer.res.mjs +37 -30
  82. package/src/bindings/Performance.res +7 -0
  83. package/src/bindings/Performance.res.mjs +21 -0
  84. package/src/bindings/Performance.resi +7 -0
  85. package/src/bindings/Viem.res +0 -41
  86. package/src/bindings/Viem.res.mjs +1 -43
  87. package/src/sources/BlockStore.res +46 -0
  88. package/src/sources/BlockStore.res.mjs +24 -0
  89. package/src/sources/EventRouter.res +36 -33
  90. package/src/sources/EventRouter.res.mjs +13 -14
  91. package/src/sources/Evm.res +83 -72
  92. package/src/sources/Evm.res.mjs +53 -69
  93. package/src/sources/EvmChain.res +18 -20
  94. package/src/sources/EvmChain.res.mjs +15 -15
  95. package/src/sources/EvmRpcClient.res +74 -5
  96. package/src/sources/EvmRpcClient.res.mjs +13 -4
  97. package/src/sources/FieldMask.res +39 -0
  98. package/src/sources/FieldMask.res.mjs +42 -0
  99. package/src/sources/Fuel.res +9 -6
  100. package/src/sources/Fuel.res.mjs +5 -10
  101. package/src/sources/HyperFuelSource.res +46 -50
  102. package/src/sources/HyperFuelSource.res.mjs +64 -54
  103. package/src/sources/HyperSync.res +39 -7
  104. package/src/sources/HyperSync.res.mjs +57 -37
  105. package/src/sources/HyperSync.resi +12 -2
  106. package/src/sources/HyperSyncClient.res +22 -111
  107. package/src/sources/HyperSyncClient.res.mjs +2 -30
  108. package/src/sources/HyperSyncHeightStream.res +1 -8
  109. package/src/sources/HyperSyncHeightStream.res.mjs +0 -2
  110. package/src/sources/HyperSyncSource.res +94 -107
  111. package/src/sources/HyperSyncSource.res.mjs +82 -86
  112. package/src/sources/Rpc.res +15 -47
  113. package/src/sources/Rpc.res.mjs +25 -56
  114. package/src/sources/RpcSource.res +320 -467
  115. package/src/sources/RpcSource.res.mjs +268 -385
  116. package/src/sources/RpcWebSocketHeightStream.res +0 -5
  117. package/src/sources/RpcWebSocketHeightStream.res.mjs +0 -2
  118. package/src/sources/SimulateSource.res +10 -4
  119. package/src/sources/SimulateSource.res.mjs +16 -6
  120. package/src/sources/Source.res +34 -6
  121. package/src/sources/SourceManager.res +77 -19
  122. package/src/sources/SourceManager.res.mjs +63 -21
  123. package/src/sources/SourceManager.resi +10 -0
  124. package/src/sources/Svm.res +31 -16
  125. package/src/sources/Svm.res.mjs +36 -13
  126. package/src/sources/SvmHyperSyncClient.res +5 -32
  127. package/src/sources/SvmHyperSyncSource.res +159 -134
  128. package/src/sources/SvmHyperSyncSource.res.mjs +164 -129
  129. package/src/sources/TransactionStore.res +49 -0
  130. package/src/sources/TransactionStore.res.mjs +30 -0
  131. package/src/tui/Tui.res +13 -16
  132. package/src/tui/Tui.res.mjs +12 -14
  133. package/svm.schema.json +49 -37
  134. package/src/bindings/Hrtime.res +0 -58
  135. package/src/bindings/Hrtime.res.mjs +0 -90
  136. package/src/bindings/Hrtime.resi +0 -30
@@ -7,26 +7,23 @@ type selectionConfig = {
7
7
  fieldSelection: HyperSyncClient.QueryTypes.fieldSelection,
8
8
  }
9
9
 
10
- let getSelectionConfig = (selection: FetchState.selection, ~chain) => {
10
+ let getSelectionConfig = (selection: FetchState.selection) => {
11
11
  let capitalizedBlockFields = Utils.Set.make()
12
12
  let capitalizedTransactionFields = Utils.Set.make()
13
13
 
14
- let staticTopicSelectionsByContract = Dict.make()
15
- let dynamicEventFiltersByContract = Dict.make()
16
- let dynamicWildcardEventFiltersByContract = Dict.make()
14
+ let topicSelectionsByContract = Dict.make()
15
+ let wildcardTopicSelectionsByContract = Dict.make()
17
16
  let noAddressesTopicSelections = []
18
17
  let contractNames = Utils.Set.make()
19
18
 
20
- selection.eventConfigs
21
- ->(Utils.magic: array<Internal.eventConfig> => array<Internal.evmEventConfig>)
22
- ->Array.forEach(({
23
- dependsOnAddresses,
24
- contractName,
25
- getEventFiltersOrThrow,
26
- selectedBlockFields,
27
- selectedTransactionFields,
28
- isWildcard,
29
- }) => {
19
+ selection.onEventRegistrations
20
+ ->(Utils.magic: array<Internal.onEventRegistration> => array<Internal.evmOnEventRegistration>)
21
+ ->Array.forEach(reg => {
22
+ let eventConfig =
23
+ reg.eventConfig->(Utils.magic: Internal.eventConfig => Internal.evmEventConfig)
24
+ let contractName = eventConfig.contractName
25
+ let {selectedBlockFields, selectedTransactionFields} = eventConfig
26
+ let {dependsOnAddresses, resolvedWhere, isWildcard} = reg
30
27
  selectedBlockFields
31
28
  ->Utils.Set.toArray
32
29
  ->Array.forEach(name =>
@@ -36,30 +33,26 @@ let getSelectionConfig = (selection: FetchState.selection, ~chain) => {
36
33
  )
37
34
  selectedTransactionFields
38
35
  ->Utils.Set.toArray
39
- ->Array.forEach(name =>
40
- capitalizedTransactionFields
41
- ->Utils.Set.add((name :> string)->Utils.String.capitalize)
42
- ->ignore
43
- )
36
+ ->Array.forEach(name => {
37
+ // transactionIndex is read off the log (the store key), so it never needs
38
+ // to be requested as a transaction column — and requesting it alone would
39
+ // pull the whole transaction table for nothing.
40
+ let fieldName = (name :> string)
41
+ if fieldName != "transactionIndex" {
42
+ capitalizedTransactionFields->Utils.Set.add(fieldName->Utils.String.capitalize)->ignore
43
+ }
44
+ })
44
45
 
45
- let eventFilters = getEventFiltersOrThrow(chain)
46
46
  if dependsOnAddresses {
47
47
  let _ = contractNames->Utils.Set.add(contractName)
48
- switch eventFilters {
49
- | Static(topicSelections) =>
50
- staticTopicSelectionsByContract->Utils.Dict.pushMany(contractName, topicSelections)
51
- | Dynamic(fn) =>
52
- (
53
- isWildcard ? dynamicWildcardEventFiltersByContract : dynamicEventFiltersByContract
54
- )->Utils.Dict.push(contractName, fn)
55
- }
48
+
49
+ (
50
+ isWildcard ? wildcardTopicSelectionsByContract : topicSelectionsByContract
51
+ )->Utils.Dict.pushMany(contractName, resolvedWhere.topicSelections)
56
52
  } else {
57
53
  noAddressesTopicSelections
58
54
  ->Array.pushMany(
59
- switch eventFilters {
60
- | Static(s) => s
61
- | Dynamic(fn) => fn([])
62
- },
55
+ resolvedWhere.topicSelections->LogSelection.materializeTopicSelections(~addresses=[]),
63
56
  )
64
57
  ->ignore
65
58
  }
@@ -90,27 +83,26 @@ let getSelectionConfig = (selection: FetchState.selection, ~chain) => {
90
83
  | None
91
84
  | Some([]) => ()
92
85
  | Some(addresses) =>
93
- switch staticTopicSelectionsByContract->Utils.Dict.dangerouslyGetNonOption(contractName) {
86
+ switch topicSelectionsByContract->Utils.Dict.dangerouslyGetNonOption(contractName) {
94
87
  | None => ()
95
88
  | Some(topicSelections) =>
96
- logSelections->Array.push(LogSelection.make(~addresses, ~topicSelections))
97
- }
98
- switch dynamicEventFiltersByContract->Utils.Dict.dangerouslyGetNonOption(contractName) {
99
- | None => ()
100
- | Some(fns) =>
101
89
  logSelections->Array.push(
102
- LogSelection.make(~addresses, ~topicSelections=fns->Array.flatMap(fn => fn(addresses))),
90
+ LogSelection.make(
91
+ ~addresses,
92
+ ~topicSelections=topicSelections->LogSelection.materializeTopicSelections(~addresses),
93
+ ),
103
94
  )
104
95
  }
105
- switch dynamicWildcardEventFiltersByContract->Utils.Dict.dangerouslyGetNonOption(
106
- contractName,
107
- ) {
96
+ // Wildcard events that filter an indexed param by registered addresses:
97
+ // the addresses fold into the topics, so the query itself stays
98
+ // address-unbound.
99
+ switch wildcardTopicSelectionsByContract->Utils.Dict.dangerouslyGetNonOption(contractName) {
108
100
  | None => ()
109
- | Some(fns) =>
101
+ | Some(topicSelections) =>
110
102
  logSelections->Array.push(
111
103
  LogSelection.make(
112
104
  ~addresses=[],
113
- ~topicSelections=fns->Array.flatMap(fn => fn(addresses)),
105
+ ~topicSelections=topicSelections->LogSelection.materializeTopicSelections(~addresses),
114
106
  ),
115
107
  )
116
108
  }
@@ -125,8 +117,7 @@ let getSelectionConfig = (selection: FetchState.selection, ~chain) => {
125
117
  }
126
118
  }
127
119
 
128
- let memoGetSelectionConfig = (~chain) =>
129
- Utils.WeakMap.memoize(selection => selection->getSelectionConfig(~chain))
120
+ let memoGetSelectionConfig = () => Utils.WeakMap.memoize(getSelectionConfig)
130
121
 
131
122
  // Surfaced by HyperSyncClient.getHeight (Rust) when HyperSync rejects the API
132
123
  // token. The corrupted-token test feeds the real server error through this
@@ -137,7 +128,7 @@ type options = {
137
128
  chain: ChainMap.Chain.t,
138
129
  endpointUrl: string,
139
130
  allEventParams: array<HyperSyncClient.Decoder.eventParamsInput>,
140
- eventRouter: EventRouter.t<Internal.evmEventConfig>,
131
+ eventRouter: EventRouter.t<Internal.evmOnEventRegistration>,
141
132
  apiToken: option<string>,
142
133
  clientTimeoutMillis: int,
143
134
  lowercaseAddresses: bool,
@@ -162,7 +153,7 @@ let make = (
162
153
  ): t => {
163
154
  let name = "HyperSync"
164
155
 
165
- let getSelectionConfig = memoGetSelectionConfig(~chain)
156
+ let getSelectionConfig = memoGetSelectionConfig()
166
157
 
167
158
  let apiToken = switch apiToken {
168
159
  | Some(token) => token
@@ -194,27 +185,24 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
194
185
  let makeEventBatchQueueItem = (
195
186
  item: HyperSyncClient.EventItems.item,
196
187
  ~params: Internal.eventParams,
197
- ~eventConfig: Internal.evmEventConfig,
188
+ ~onEventRegistration: Internal.evmOnEventRegistration,
198
189
  ): Internal.item => {
199
- let {block, transaction, logIndex, srcAddress} = item
190
+ let {transactionIndex, logIndex, srcAddress} = item
200
191
  let chainId = chain->ChainMap.Chain.toChainId
201
192
 
202
193
  Internal.Event({
203
- eventConfig: (eventConfig :> Internal.eventConfig),
204
- timestamp: block.timestamp->Option.getUnsafe,
194
+ onEventRegistration: (onEventRegistration :> Internal.onEventRegistration),
205
195
  chain,
206
- blockNumber: block.number->Option.getUnsafe,
207
- blockHash: block.hash->Option.getUnsafe,
196
+ blockNumber: item.blockNumber,
208
197
  logIndex,
198
+ transactionIndex,
199
+ // `block` and `transaction` are omitted; they're materialised from the
200
+ // per-chain stores onto the payload at batch prep.
209
201
  payload: {
210
- contractName: eventConfig.contractName,
211
- eventName: eventConfig.name,
202
+ contractName: onEventRegistration.eventConfig.contractName,
203
+ eventName: onEventRegistration.eventConfig.name,
212
204
  chainId,
213
205
  params,
214
- transaction: transaction->(
215
- Utils.magic: HyperSyncClient.ResponseTypes.transaction => Internal.eventTransaction
216
- ),
217
- block: block->(Utils.magic: HyperSyncClient.ResponseTypes.block => Internal.eventBlock),
218
206
  srcAddress,
219
207
  logIndex,
220
208
  }->Evm.fromPayload,
@@ -225,14 +213,15 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
225
213
  ~fromBlock,
226
214
  ~toBlock,
227
215
  ~addressesByContractName,
228
- ~indexingAddresses,
216
+ ~contractNameByAddress,
229
217
  ~knownHeight,
230
218
  ~partitionId as _,
231
219
  ~selection,
220
+ ~itemsTarget,
232
221
  ~retry,
233
222
  ~logger,
234
223
  ) => {
235
- let totalTimeRef = Hrtime.makeTimer()
224
+ let totalTimeRef = Performance.now()
236
225
 
237
226
  let selectionConfig = selection->getSelectionConfig
238
227
 
@@ -241,20 +230,16 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
241
230
  exn->ErrorHandling.mkLogAndRaise(~logger, ~msg="Failed getting log selection for the query")
242
231
  }
243
232
 
244
- let startFetchingBatchTimeRef = Hrtime.makeTimer()
233
+ let startFetchingBatchTimeRef = Performance.now()
245
234
 
246
235
  //fetch batch
247
- Prometheus.SourceRequestCount.increment(
248
- ~sourceName=name,
249
- ~chainId=chain->ChainMap.Chain.toChainId,
250
- ~method="getLogs",
251
- )
252
236
  let pageUnsafe = try await HyperSync.GetLogs.query(
253
237
  ~client,
254
238
  ~fromBlock,
255
239
  ~toBlock,
256
240
  ~logSelections,
257
241
  ~fieldSelection=selectionConfig.fieldSelection,
242
+ ~maxNumLogs=itemsTarget,
258
243
  ) catch {
259
244
  | HyperSync.GetLogs.Error(error) =>
260
245
  throw(
@@ -301,7 +286,8 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
301
286
  )
302
287
  }
303
288
 
304
- let pageFetchTime = startFetchingBatchTimeRef->Hrtime.timeSince->Hrtime.toSecondsFloat
289
+ let pageFetchTime = startFetchingBatchTimeRef->Performance.secondsSince
290
+ let requestStats = [{Source.method: "getLogs", seconds: pageFetchTime}]
305
291
 
306
292
  //set height and next from block
307
293
  let knownHeight = pageUnsafe.archiveHeight
@@ -311,23 +297,30 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
311
297
  //In the query
312
298
  let heighestBlockQueried = pageUnsafe.nextBlock - 1
313
299
 
314
- let parsingTimeRef = Hrtime.makeTimer()
300
+ let parsingTimeRef = Performance.now()
315
301
 
316
302
  //Parse page items into queue items
317
303
  let parsedQueueItems = []
318
304
 
305
+ // Block headers are returned once per number; items reference them by blockNumber.
306
+ let blocksByNumber = Utils.Map.make()
307
+ pageUnsafe.blocks->Array.forEach(block => {
308
+ blocksByNumber->Utils.Map.set(block.number, block)->ignore
309
+ })
310
+ let getBlock = blockNumber => blocksByNumber->Utils.Map.unsafeGet(blockNumber)
311
+
319
312
  let handleDecodeFailure = (
320
- ~eventConfig: Internal.evmEventConfig,
313
+ ~onEventRegistration: Internal.evmOnEventRegistration,
321
314
  ~logIndex,
322
315
  ~blockNumber,
323
316
  ~chainId,
324
317
  ~exn,
325
318
  ) => {
326
- if !eventConfig.isWildcard {
319
+ if !onEventRegistration.isWildcard {
327
320
  //Wildcard events can be parsed as undefined if the number of topics
328
321
  //don't match the event with the given topic0
329
322
  //Non wildcard events should be expected to be parsed
330
- let msg = `Event ${eventConfig.name} was unexpectedly parsed as undefined`
323
+ let msg = `Event ${onEventRegistration.eventConfig.name} was unexpectedly parsed as undefined`
331
324
  let logger = Logging.createChildFrom(
332
325
  ~logger,
333
326
  ~params={
@@ -349,24 +342,25 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
349
342
  ~sighash=item.topic0->EvmTypes.Hex.toString,
350
343
  ~topicCount=item.topicCount,
351
344
  ),
352
- ~indexingAddresses,
345
+ ~contractNameByAddress,
353
346
  ~contractAddress=item.srcAddress,
354
- ~blockNumber=item.block.number->Option.getUnsafe,
355
347
  )
356
348
 
357
349
  switch maybeEventConfig {
358
350
  | None => () //ignore events that aren't registered
359
- | Some(eventConfig) =>
351
+ | Some(onEventRegistration) =>
360
352
  switch item.params
361
353
  ->Nullable.toOption
362
- ->Option.flatMap(Dict.get(_, eventConfig.contractName)) {
354
+ ->Option.flatMap(Dict.get(_, onEventRegistration.eventConfig.contractName)) {
363
355
  | Some(params) =>
364
- parsedQueueItems->Array.push(makeEventBatchQueueItem(item, ~params, ~eventConfig))->ignore
356
+ parsedQueueItems
357
+ ->Array.push(makeEventBatchQueueItem(item, ~params, ~onEventRegistration))
358
+ ->ignore
365
359
  | None =>
366
360
  handleDecodeFailure(
367
- ~eventConfig,
361
+ ~onEventRegistration,
368
362
  ~logIndex=item.logIndex,
369
- ~blockNumber=item.block.number->Option.getUnsafe,
363
+ ~blockNumber=item.blockNumber,
370
364
  ~chainId,
371
365
  ~exn=UndefinedValue,
372
366
  )
@@ -374,19 +368,17 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
374
368
  }
375
369
  })
376
370
 
377
- let parsingTimeElapsed = parsingTimeRef->Hrtime.timeSince->Hrtime.toSecondsFloat
371
+ let parsingTimeElapsed = parsingTimeRef->Performance.secondsSince
378
372
 
379
373
  // Collect (blockNumber, blockHash) pairs we already have from the response —
380
- // one per item's block plus, when present, the rollbackGuard's head block
374
+ // one per returned block plus, when present, the rollbackGuard's head block
381
375
  // and the parent of the range's first block. Duplicates are allowed; reorg
382
376
  // detection notices same-block-number-different-hash collisions itself.
383
377
  let blockHashes = []
384
- pageUnsafe.items->Array.forEach(({block}) => {
385
- switch (block.number, block.hash) {
386
- | (Some(blockNumber), Some(blockHash)) =>
387
- blockHashes->Array.push({ReorgDetection.blockNumber, blockHash})->ignore
388
- | _ => ()
389
- }
378
+ pageUnsafe.blocks->Array.forEach(block => {
379
+ blockHashes
380
+ ->Array.push({ReorgDetection.blockNumber: block.number, blockHash: block.hash})
381
+ ->ignore
390
382
  })
391
383
  switch pageUnsafe.rollbackGuard {
392
384
  | None => ()
@@ -409,13 +401,13 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
409
401
  | Some({timestamp}) => timestamp
410
402
  | None =>
411
403
  switch pageUnsafe.items->Array.get(pageUnsafe.items->Array.length - 1) {
412
- | Some({block}) if block.number->Option.getUnsafe == heighestBlockQueried =>
413
- block.timestamp->Option.getUnsafe
404
+ | Some(item) if item.blockNumber == heighestBlockQueried =>
405
+ getBlock(item.blockNumber).timestamp
414
406
  | _ => 0
415
407
  }
416
408
  }
417
409
 
418
- let totalTimeElapsed = totalTimeRef->Hrtime.timeSince->Hrtime.toSecondsFloat
410
+ let totalTimeElapsed = totalTimeRef->Performance.secondsSince
419
411
 
420
412
  let stats = {
421
413
  totalTimeElapsed,
@@ -426,11 +418,14 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
426
418
  {
427
419
  latestFetchedBlockTimestamp,
428
420
  parsedQueueItems,
421
+ transactionStore: Some(pageUnsafe.transactionStore),
422
+ blockStore: Some(pageUnsafe.blockStore),
429
423
  latestFetchedBlockNumber: heighestBlockQueried,
430
424
  stats,
431
425
  knownHeight,
432
426
  blockHashes,
433
427
  fromBlockQueried: fromBlock,
428
+ requestStats,
434
429
  }
435
430
  }
436
431
 
@@ -441,7 +436,10 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
441
436
  ~sourceName=name,
442
437
  ~chainId=chain->ChainMap.Chain.toChainId,
443
438
  ~logger,
444
- )->Promise.thenResolve(HyperSync.mapExn)
439
+ )->Promise.thenResolve(((queryRes, requestStats)) => {
440
+ Source.result: queryRes->HyperSync.mapExn,
441
+ requestStats,
442
+ })
445
443
 
446
444
  {
447
445
  name,
@@ -451,8 +449,8 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
451
449
  poweredByHyperSync: true,
452
450
  getBlockHashes,
453
451
  getHeightOrThrow: async () => {
454
- let timerRef = Hrtime.makeTimer()
455
- let result = try {
452
+ let timerRef = Performance.now()
453
+ let height = try {
456
454
  await client.getHeight()
457
455
  } catch {
458
456
  | JsExn(e) =>
@@ -465,19 +463,8 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
465
463
  | _ => throw(JsExn(e))
466
464
  }
467
465
  }
468
- let seconds = timerRef->Hrtime.timeSince->Hrtime.toSecondsFloat
469
- Prometheus.SourceRequestCount.increment(
470
- ~sourceName=name,
471
- ~chainId=chain->ChainMap.Chain.toChainId,
472
- ~method="getHeight",
473
- )
474
- Prometheus.SourceRequestCount.addSeconds(
475
- ~sourceName=name,
476
- ~chainId=chain->ChainMap.Chain.toChainId,
477
- ~method="getHeight",
478
- ~seconds,
479
- )
480
- result
466
+ let seconds = timerRef->Performance.secondsSince
467
+ {height, requestStats: [{method: "getHeight", seconds}]}
481
468
  },
482
469
  getItemsOrThrow,
483
470
  createHeightSubscription: (~onHeight) =>