envio 3.3.0-alpha.7 → 3.3.0-alpha.9

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 (95) hide show
  1. package/index.d.ts +23 -5
  2. package/package.json +6 -6
  3. package/src/Api.res +1 -1
  4. package/src/Api.res.mjs +1 -1
  5. package/src/ChainFetching.res +21 -10
  6. package/src/ChainFetching.res.mjs +11 -10
  7. package/src/ChainState.res +401 -171
  8. package/src/ChainState.res.mjs +270 -104
  9. package/src/ChainState.resi +19 -7
  10. package/src/Config.res +11 -27
  11. package/src/Config.res.mjs +8 -7
  12. package/src/Core.res +7 -0
  13. package/src/CrossChainState.res +82 -82
  14. package/src/CrossChainState.res.mjs +52 -59
  15. package/src/CrossChainState.resi +1 -1
  16. package/src/Ecosystem.res +3 -3
  17. package/src/Ecosystem.res.mjs +3 -3
  18. package/src/Envio.res +14 -9
  19. package/src/EnvioGlobal.res +53 -0
  20. package/src/EnvioGlobal.res.mjs +31 -0
  21. package/src/EventConfigBuilder.res +117 -69
  22. package/src/EventConfigBuilder.res.mjs +71 -34
  23. package/src/EventProcessing.res +19 -15
  24. package/src/EventProcessing.res.mjs +13 -12
  25. package/src/FetchState.res +410 -185
  26. package/src/FetchState.res.mjs +268 -253
  27. package/src/HandlerLoader.res +8 -113
  28. package/src/HandlerLoader.res.mjs +2 -88
  29. package/src/HandlerRegister.res +518 -144
  30. package/src/HandlerRegister.res.mjs +285 -133
  31. package/src/HandlerRegister.resi +24 -8
  32. package/src/IndexerState.res +6 -3
  33. package/src/IndexerState.res.mjs +3 -3
  34. package/src/IndexerState.resi +1 -1
  35. package/src/IndexingAddresses.res +10 -7
  36. package/src/IndexingAddresses.res.mjs +8 -7
  37. package/src/IndexingAddresses.resi +3 -1
  38. package/src/Internal.res +104 -39
  39. package/src/Internal.res.mjs +11 -1
  40. package/src/LogSelection.res +102 -165
  41. package/src/LogSelection.res.mjs +101 -116
  42. package/src/Main.res +49 -164
  43. package/src/Main.res.mjs +39 -104
  44. package/src/Metrics.res +43 -2
  45. package/src/Metrics.res.mjs +39 -3
  46. package/src/Prometheus.res +0 -35
  47. package/src/Prometheus.res.mjs +33 -68
  48. package/src/RawEvent.res +7 -2
  49. package/src/RawEvent.res.mjs +4 -4
  50. package/src/RollbackCommit.res +4 -1
  51. package/src/RollbackCommit.res.mjs +3 -2
  52. package/src/SimulateItems.res +20 -7
  53. package/src/SimulateItems.res.mjs +7 -11
  54. package/src/TestIndexer.res +1 -1
  55. package/src/TestIndexer.res.mjs +1 -1
  56. package/src/sources/BlockStore.res +46 -0
  57. package/src/sources/BlockStore.res.mjs +24 -0
  58. package/src/sources/EventRouter.res +19 -12
  59. package/src/sources/EventRouter.res.mjs +7 -5
  60. package/src/sources/Evm.res +58 -5
  61. package/src/sources/Evm.res.mjs +44 -5
  62. package/src/sources/EvmChain.res +14 -18
  63. package/src/sources/EvmChain.res.mjs +12 -13
  64. package/src/sources/FieldMask.res +39 -0
  65. package/src/sources/FieldMask.res.mjs +42 -0
  66. package/src/sources/Fuel.res +8 -3
  67. package/src/sources/Fuel.res.mjs +5 -4
  68. package/src/sources/HyperFuelSource.res +31 -40
  69. package/src/sources/HyperFuelSource.res.mjs +52 -44
  70. package/src/sources/HyperSync.res +31 -9
  71. package/src/sources/HyperSync.res.mjs +47 -31
  72. package/src/sources/HyperSync.resi +10 -3
  73. package/src/sources/HyperSyncClient.res +15 -4
  74. package/src/sources/HyperSyncHeightStream.res +1 -8
  75. package/src/sources/HyperSyncHeightStream.res.mjs +0 -2
  76. package/src/sources/HyperSyncSource.res +58 -90
  77. package/src/sources/HyperSyncSource.res.mjs +57 -66
  78. package/src/sources/RpcSource.res +118 -120
  79. package/src/sources/RpcSource.res.mjs +92 -74
  80. package/src/sources/RpcWebSocketHeightStream.res +0 -5
  81. package/src/sources/RpcWebSocketHeightStream.res.mjs +0 -2
  82. package/src/sources/SimulateSource.res +5 -3
  83. package/src/sources/SimulateSource.res.mjs +12 -4
  84. package/src/sources/Source.res +19 -5
  85. package/src/sources/SourceManager.res +60 -10
  86. package/src/sources/SourceManager.res.mjs +54 -8
  87. package/src/sources/SourceManager.resi +10 -0
  88. package/src/sources/Svm.res +14 -10
  89. package/src/sources/Svm.res.mjs +23 -6
  90. package/src/sources/SvmHyperSyncClient.res +5 -6
  91. package/src/sources/SvmHyperSyncSource.res +82 -41
  92. package/src/sources/SvmHyperSyncSource.res.mjs +86 -41
  93. package/src/sources/TransactionStore.res +6 -107
  94. package/src/sources/TransactionStore.res.mjs +5 -86
  95. package/svm.schema.json +19 -0
@@ -48,18 +48,16 @@ let getKnownRawBlockWithBackoff = async (
48
48
  ~chain,
49
49
  ~blockNumber,
50
50
  ~backoffMsOnFailure,
51
+ ~recordRequest: (~method: string, ~seconds: float) => unit,
51
52
  ) => {
52
53
  let currentBackoff = ref(backoffMsOnFailure)
53
54
  let result = ref(None)
54
55
 
55
56
  while result.contents->Option.isNone {
56
- Prometheus.SourceRequestCount.increment(
57
- ~sourceName,
58
- ~chainId=chain->ChainMap.Chain.toChainId,
59
- ~method="eth_getBlockByNumber",
60
- )
57
+ let timerRef = Performance.now()
61
58
  switch await getKnownRawBlock(~client, ~blockNumber) {
62
59
  | exception err =>
60
+ recordRequest(~method="eth_getBlockByNumber", ~seconds=timerRef->Performance.secondsSince)
63
61
  Logging.warn({
64
62
  "err": err->Utils.prettifyExn,
65
63
  "msg": `Issue while running fetching batch of events from the RPC. Will wait ${currentBackoff.contents->Int.toString}ms and try again.`,
@@ -69,7 +67,9 @@ let getKnownRawBlockWithBackoff = async (
69
67
  })
70
68
  await Time.resolvePromiseAfterDelay(~delayMilliseconds=currentBackoff.contents)
71
69
  currentBackoff := currentBackoff.contents * 2
72
- | json => result := Some(json)
70
+ | json =>
71
+ recordRequest(~method="eth_getBlockByNumber", ~seconds=timerRef->Performance.secondsSince)
72
+ result := Some(json)
73
73
  }
74
74
  }
75
75
  result.contents->Option.getOrThrow
@@ -300,8 +300,7 @@ let getNextPage = (
300
300
  ~rpcClient: EvmRpcClient.t,
301
301
  ~mutSuggestedBlockIntervals,
302
302
  ~partitionId,
303
- ~sourceName,
304
- ~chainId,
303
+ ~recordRequest: (~method: string, ~seconds: float) => unit,
305
304
  ): promise<eventBatchQuery> => {
306
305
  //If the query hangs for longer than this, reject this promise to reduce the block interval
307
306
  let queryTimoutPromise =
@@ -314,7 +313,7 @@ let getNextPage = (
314
313
  let latestFetchedBlockPromise = loadBlock(toBlock)
315
314
 
316
315
  let queryLogs = ({addresses, topicQuery}: logSelection) => {
317
- Prometheus.SourceRequestCount.increment(~sourceName, ~chainId, ~method="eth_getLogs")
316
+ let timerRef = Performance.now()
318
317
  rpcClient.getLogs({
319
318
  fromBlock,
320
319
  toBlock,
@@ -326,6 +325,9 @@ let getNextPage = (
326
325
  | Multiple(topics) => Nullable.make(topics)
327
326
  }
328
327
  ),
328
+ })->Promise.thenResolve(items => {
329
+ recordRequest(~method="eth_getLogs", ~seconds=timerRef->Performance.secondsSince)
330
+ items
329
331
  })
330
332
  }
331
333
 
@@ -410,13 +412,13 @@ type selectionConfig = {
410
412
  ) => array<logSelection>,
411
413
  }
412
414
 
413
- let getSelectionConfig = (selection: FetchState.selection, ~chain) => {
414
- let evmEventConfigs =
415
- selection.eventConfigs->(
416
- Utils.magic: array<Internal.eventConfig> => array<Internal.evmEventConfig>
415
+ let getSelectionConfig = (selection: FetchState.selection) => {
416
+ let evmOnEventRegistrations =
417
+ selection.onEventRegistrations->(
418
+ Utils.magic: array<Internal.onEventRegistration> => array<Internal.evmOnEventRegistration>
417
419
  )
418
420
 
419
- if evmEventConfigs->Utils.Array.isEmpty {
421
+ if evmOnEventRegistrations->Utils.Array.isEmpty {
420
422
  throw(
421
423
  Source.GetItemsError(
422
424
  UnsupportedSelection({
@@ -434,36 +436,23 @@ let getSelectionConfig = (selection: FetchState.selection, ~chain) => {
434
436
  // re-applies it). Pure-wildcard events carry no address constraint, so they're
435
437
  // pooled and resolved once.
436
438
  let noAddressTopicSelections = []
437
- let staticByContract = Dict.make()
438
- let dynamicByContract = Dict.make()
439
- let dynamicWildcardByContract = Dict.make()
439
+ let byContract = Dict.make()
440
+ let wildcardByContract = Dict.make()
440
441
  let contractNames = Utils.Set.make()
441
442
 
442
- evmEventConfigs->Array.forEach(({
443
- contractName,
444
- isWildcard,
445
- dependsOnAddresses,
446
- getEventFiltersOrThrow,
447
- }) => {
448
- let eventFilters = getEventFiltersOrThrow(chain)
443
+ evmOnEventRegistrations->Array.forEach(reg => {
444
+ let contractName = reg.eventConfig.contractName
445
+ let {isWildcard, dependsOnAddresses, resolvedWhere} = reg
449
446
  if dependsOnAddresses {
450
447
  contractNames->Utils.Set.add(contractName)->ignore
451
- switch eventFilters {
452
- | Internal.Static(topicSelections) =>
453
- staticByContract->Utils.Dict.pushMany(contractName, topicSelections)
454
- | Dynamic(fn) =>
455
- (isWildcard ? dynamicWildcardByContract : dynamicByContract)->Utils.Dict.push(
456
- contractName,
457
- fn,
458
- )
459
- }
448
+ (isWildcard ? wildcardByContract : byContract)->Utils.Dict.pushMany(
449
+ contractName,
450
+ resolvedWhere.topicSelections,
451
+ )
460
452
  } else {
461
453
  noAddressTopicSelections
462
454
  ->Array.pushMany(
463
- switch eventFilters {
464
- | Static(s) => s
465
- | Dynamic(fn) => fn([])
466
- },
455
+ resolvedWhere.topicSelections->LogSelection.materializeTopicSelections(~addresses=[]),
467
456
  )
468
457
  ->ignore
469
458
  }
@@ -492,30 +481,30 @@ let getSelectionConfig = (selection: FetchState.selection, ~chain) => {
492
481
  | None
493
482
  | Some([]) => ()
494
483
  | Some(addresses) =>
495
- // Static + dynamic non-wildcard filters, scoped to this contract's addresses.
496
- let addressedTopicSelections = []
497
- switch staticByContract->Utils.Dict.dangerouslyGetNonOption(contractName) {
498
- | Some(s) => addressedTopicSelections->Array.pushMany(s)->ignore
499
- | None => ()
500
- }
501
- switch dynamicByContract->Utils.Dict.dangerouslyGetNonOption(contractName) {
502
- | Some(fns) =>
503
- fns->Array.forEach(fn =>
504
- addressedTopicSelections->Array.pushMany(fn(addresses))->ignore
484
+ // Non-wildcard filters, scoped to this contract's addresses.
485
+ switch byContract->Utils.Dict.dangerouslyGetNonOption(contractName) {
486
+ | Some(topicSelections) =>
487
+ logSelections
488
+ ->Array.pushMany(
489
+ toLogSelections(
490
+ ~addresses=Some(addresses),
491
+ topicSelections->LogSelection.materializeTopicSelections(~addresses),
492
+ ),
505
493
  )
494
+ ->ignore
506
495
  | None => ()
507
496
  }
508
- logSelections
509
- ->Array.pushMany(toLogSelections(~addresses=Some(addresses), addressedTopicSelections))
510
- ->ignore
511
497
 
512
- // Dynamic wildcard-by-address filters fold the address into the topics,
498
+ // Wildcard-by-address filters fold the address into the topics,
513
499
  // so they still match any address.
514
- switch dynamicWildcardByContract->Utils.Dict.dangerouslyGetNonOption(contractName) {
515
- | Some(fns) =>
500
+ switch wildcardByContract->Utils.Dict.dangerouslyGetNonOption(contractName) {
501
+ | Some(topicSelections) =>
516
502
  logSelections
517
503
  ->Array.pushMany(
518
- toLogSelections(~addresses=None, fns->Array.flatMap(fn => fn(addresses))),
504
+ toLogSelections(
505
+ ~addresses=None,
506
+ topicSelections->LogSelection.materializeTopicSelections(~addresses),
507
+ ),
519
508
  )
520
509
  ->ignore
521
510
  | None => ()
@@ -531,8 +520,7 @@ let getSelectionConfig = (selection: FetchState.selection, ~chain) => {
531
520
  }
532
521
  }
533
522
 
534
- let memoGetSelectionConfig = (~chain) =>
535
- Utils.WeakMap.memoize(selection => selection->getSelectionConfig(~chain))
523
+ let memoGetSelectionConfig = () => Utils.WeakMap.memoize(getSelectionConfig)
536
524
 
537
525
  // Type-erase a schema for storage in the field registry
538
526
  external toFieldSchema: S.t<'a> => S.t<JSON.t> = "%identity"
@@ -674,11 +662,9 @@ let makeThrowingGetEventBlock = (
674
662
  }
675
663
  }
676
664
 
677
- // `number`, `timestamp` and `hash` are always part of the selected block
678
- // fields, so they can be read from the assembled block at item construction.
665
+ // `number` is always part of the selected block fields, so it can be read
666
+ // from the assembled block for the item's own `blockNumber`.
679
667
  @get external getBlockNumber: Internal.eventBlock => int = "number"
680
- @get external getBlockTimestamp: Internal.eventBlock => int = "timestamp"
681
- @get external getBlockHash: Internal.eventBlock => string = "hash"
682
668
 
683
669
  // Field source classification for RPC calls
684
670
  type fieldSource = TransactionOnly | ReceiptOnly | Both
@@ -994,7 +980,7 @@ type options = {
994
980
  syncConfig: Config.sourceSync,
995
981
  url: string,
996
982
  chain: ChainMap.Chain.t,
997
- eventRouter: EventRouter.t<Internal.evmEventConfig>,
983
+ eventRouter: EventRouter.t<Internal.evmOnEventRegistration>,
998
984
  allEventParams: array<HyperSyncClient.Decoder.eventParamsInput>,
999
985
  lowercaseAddresses: bool,
1000
986
  ws?: string,
@@ -1024,7 +1010,7 @@ let make = (
1024
1010
  }
1025
1011
  let name = `RPC (${urlHost})`
1026
1012
 
1027
- let getSelectionConfig = memoGetSelectionConfig(~chain)
1013
+ let getSelectionConfig = memoGetSelectionConfig()
1028
1014
 
1029
1015
  // Per-partition adaptive block interval (AIMD), keyed by partitionId. The
1030
1016
  // `max` key holds a source-wide ceiling that only ever tightens, set by
@@ -1040,15 +1026,37 @@ let make = (
1040
1026
  ~headers?,
1041
1027
  )
1042
1028
 
1029
+ // Requests are made from shared, memoized loaders, so they can't be
1030
+ // attributed to a single getItemsOrThrow/getHeightOrThrow/getBlockHashes
1031
+ // call at its call site. Every actual request (cache/dedup hits never reach
1032
+ // recordRequest) pushes here; each method drains whatever is pending when it
1033
+ // returns. Since a push always lands in exactly one drain, per-source totals
1034
+ // stay exact even with concurrent in-flight calls — which call happens to
1035
+ // drain a given entry doesn't matter, since SourceManager aggregates by
1036
+ // (source, method) regardless of which call returned it.
1037
+ let pendingRequestStats: array<Source.requestStat> = []
1038
+ let recordRequest = (~method, ~seconds) => {
1039
+ pendingRequestStats->Array.push({Source.method, seconds})->ignore
1040
+ }
1041
+ let drainRequestStats = () => {
1042
+ let stats = pendingRequestStats->Utils.Array.copy
1043
+ pendingRequestStats->Utils.Array.clearInPlace
1044
+ stats
1045
+ }
1046
+
1043
1047
  let makeTransactionLoader = () =>
1044
1048
  LazyLoader.make(
1045
1049
  ~loaderFn=transactionHash => {
1046
- Prometheus.SourceRequestCount.increment(
1047
- ~sourceName=name,
1048
- ~chainId=chain->ChainMap.Chain.toChainId,
1049
- ~method="eth_getTransactionByHash",
1050
- )
1051
- Rpc.GetTransactionByHash.rawRoute->Rest.fetch(transactionHash, ~client)
1050
+ let timerRef = Performance.now()
1051
+ Rpc.GetTransactionByHash.rawRoute
1052
+ ->Rest.fetch(transactionHash, ~client)
1053
+ ->Promise.thenResolve(res => {
1054
+ recordRequest(
1055
+ ~method="eth_getTransactionByHash",
1056
+ ~seconds=timerRef->Performance.secondsSince,
1057
+ )
1058
+ res
1059
+ })
1052
1060
  },
1053
1061
  ~onError=(am, ~exn) => {
1054
1062
  Logging.error({
@@ -1076,6 +1084,7 @@ let make = (
1076
1084
  ~chain,
1077
1085
  ~backoffMsOnFailure=1000,
1078
1086
  ~blockNumber,
1087
+ ~recordRequest,
1079
1088
  )
1080
1089
  },
1081
1090
  ~onError=(am, ~exn) => {
@@ -1098,12 +1107,16 @@ let make = (
1098
1107
  let makeReceiptLoader = () =>
1099
1108
  LazyLoader.make(
1100
1109
  ~loaderFn=transactionHash => {
1101
- Prometheus.SourceRequestCount.increment(
1102
- ~sourceName=name,
1103
- ~chainId=chain->ChainMap.Chain.toChainId,
1104
- ~method="eth_getTransactionReceipt",
1105
- )
1106
- Rpc.GetTransactionReceipt.rawRoute->Rest.fetch(transactionHash, ~client)
1110
+ let timerRef = Performance.now()
1111
+ Rpc.GetTransactionReceipt.rawRoute
1112
+ ->Rest.fetch(transactionHash, ~client)
1113
+ ->Promise.thenResolve(res => {
1114
+ recordRequest(
1115
+ ~method="eth_getTransactionReceipt",
1116
+ ~seconds=timerRef->Performance.secondsSince,
1117
+ )
1118
+ res
1119
+ })
1107
1120
  },
1108
1121
  ~onError=(am, ~exn) => {
1109
1122
  Logging.error({
@@ -1211,8 +1224,7 @@ let make = (
1211
1224
  ~rpcClient,
1212
1225
  ~mutSuggestedBlockIntervals,
1213
1226
  ~partitionId,
1214
- ~sourceName=name,
1215
- ~chainId=chain->ChainMap.Chain.toChainId,
1227
+ ~recordRequest,
1216
1228
  )
1217
1229
 
1218
1230
  let executedBlockInterval = suggestedToBlock - fromBlock + 1
@@ -1246,7 +1258,11 @@ let make = (
1246
1258
  ~contractAddress=routedAddress,
1247
1259
  ) {
1248
1260
  | None => None
1249
- | Some(eventConfig) =>
1261
+ | Some(onEventRegistration) =>
1262
+ let eventConfig =
1263
+ onEventRegistration.eventConfig->(
1264
+ Utils.magic: Internal.eventConfig => Internal.evmEventConfig
1265
+ )
1250
1266
  switch maybeDecodedEvent
1251
1267
  ->Nullable.toOption
1252
1268
  ->Option.flatMap(Dict.get(_, eventConfig.contractName)) {
@@ -1293,10 +1309,8 @@ let make = (
1293
1309
  }
1294
1310
 
1295
1311
  Internal.Event({
1296
- eventConfig: (eventConfig :> Internal.eventConfig),
1297
- timestamp: block->getBlockTimestamp,
1312
+ onEventRegistration: (onEventRegistration :> Internal.onEventRegistration),
1298
1313
  blockNumber: block->getBlockNumber,
1299
- blockHash: block->getBlockHash,
1300
1314
  chain,
1301
1315
  logIndex: log.logIndex,
1302
1316
  transactionIndex: log.transactionIndex,
@@ -1350,14 +1364,16 @@ let make = (
1350
1364
  latestFetchedBlockTimestamp: latestFetchedBlockInfo.timestamp,
1351
1365
  latestFetchedBlockNumber: latestFetchedBlockInfo.number,
1352
1366
  parsedQueueItems,
1353
- // RPC keeps the transaction inline on the payload; no store page.
1367
+ // RPC keeps the transaction and block inline on the payload; no store pages.
1354
1368
  transactionStore: None,
1369
+ blockStore: None,
1355
1370
  stats: {
1356
1371
  totalTimeElapsed: totalTimeElapsed,
1357
1372
  },
1358
1373
  knownHeight,
1359
1374
  blockHashes,
1360
1375
  fromBlockQueried: fromBlock,
1376
+ requestStats: drainRequestStats(),
1361
1377
  }
1362
1378
  }
1363
1379
 
@@ -1374,21 +1390,25 @@ let make = (
1374
1390
  ->Array.map(blockNum => blockLoader.contents->LazyLoader.get(blockNum))
1375
1391
  ->Promise.all
1376
1392
  ->Promise.thenResolve(rawBlocks => {
1377
- rawBlocks
1378
- ->Array.map(json => {
1379
- let b = parseBlockInfo(json)
1380
-
1381
- (
1382
- {
1383
- blockNumber: b.number,
1384
- blockHash: b.hash,
1385
- blockTimestamp: b.timestamp,
1386
- }: ReorgDetection.blockDataWithTimestamp
1387
- )
1388
- })
1389
- ->Ok
1393
+ let result =
1394
+ rawBlocks
1395
+ ->Array.map(json => {
1396
+ let b = parseBlockInfo(json)
1397
+
1398
+ (
1399
+ {
1400
+ blockNumber: b.number,
1401
+ blockHash: b.hash,
1402
+ blockTimestamp: b.timestamp,
1403
+ }: ReorgDetection.blockDataWithTimestamp
1404
+ )
1405
+ })
1406
+ ->Ok
1407
+ {Source.result, requestStats: drainRequestStats()}
1390
1408
  })
1391
- ->Promise.catch(exn => exn->Error->Promise.resolve)
1409
+ ->Promise.catch(exn =>
1410
+ {Source.result: Error(exn), requestStats: drainRequestStats()}->Promise.resolve
1411
+ )
1392
1412
  }
1393
1413
 
1394
1414
  let createHeightSubscription =
@@ -1410,33 +1430,11 @@ let make = (
1410
1430
  await rpcClient.getHeight()
1411
1431
  } catch {
1412
1432
  | exn =>
1413
- let seconds = timerRef->Performance.secondsSince
1414
- Prometheus.SourceRequestCount.increment(
1415
- ~sourceName=name,
1416
- ~chainId=chain->ChainMap.Chain.toChainId,
1417
- ~method="eth_blockNumber",
1418
- )
1419
- Prometheus.SourceRequestCount.addSeconds(
1420
- ~sourceName=name,
1421
- ~chainId=chain->ChainMap.Chain.toChainId,
1422
- ~method="eth_blockNumber",
1423
- ~seconds,
1424
- )
1433
+ recordRequest(~method="eth_blockNumber", ~seconds=timerRef->Performance.secondsSince)
1425
1434
  exn->throw
1426
1435
  }
1427
- let seconds = timerRef->Performance.secondsSince
1428
- Prometheus.SourceRequestCount.increment(
1429
- ~sourceName=name,
1430
- ~chainId=chain->ChainMap.Chain.toChainId,
1431
- ~method="eth_blockNumber",
1432
- )
1433
- Prometheus.SourceRequestCount.addSeconds(
1434
- ~sourceName=name,
1435
- ~chainId=chain->ChainMap.Chain.toChainId,
1436
- ~method="eth_blockNumber",
1437
- ~seconds,
1438
- )
1439
- height
1436
+ recordRequest(~method="eth_blockNumber", ~seconds=timerRef->Performance.secondsSince)
1437
+ {height, requestStats: drainRequestStats()}
1440
1438
  },
1441
1439
  getItemsOrThrow,
1442
1440
  ?createHeightSubscription,