envio 3.1.0 → 3.1.2

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "envio",
3
- "version": "3.1.0",
3
+ "version": "3.1.2",
4
4
  "type": "module",
5
5
  "description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
6
6
  "bin": "./bin.mjs",
@@ -70,10 +70,10 @@
70
70
  "tsx": "4.21.0"
71
71
  },
72
72
  "optionalDependencies": {
73
- "envio-linux-x64": "3.1.0",
74
- "envio-linux-x64-musl": "3.1.0",
75
- "envio-linux-arm64": "3.1.0",
76
- "envio-darwin-x64": "3.1.0",
77
- "envio-darwin-arm64": "3.1.0"
73
+ "envio-linux-x64": "3.1.2",
74
+ "envio-linux-x64-musl": "3.1.2",
75
+ "envio-linux-arm64": "3.1.2",
76
+ "envio-darwin-x64": "3.1.2",
77
+ "envio-darwin-arm64": "3.1.2"
78
78
  }
79
79
  }
@@ -555,6 +555,63 @@ let blockItemLogIndex = 16777216
555
555
 
556
556
  let numAddresses = fetchState => fetchState.indexingAddresses->Utils.Dict.size
557
557
 
558
+ // Appends Block items produced by the onBlock handlers for every block in
559
+ // (fromBlock, maxBlockNumber] into mutItems and returns the new
560
+ // latestOnBlockBlockNumber pointer. targetBufferSize bounds how many items
561
+ // are generated at once to prevent OOM.
562
+ let appendOnBlockItems = (
563
+ ~mutItems: array<Internal.item>,
564
+ ~onBlockConfigs: array<Internal.onBlockConfig>,
565
+ ~indexerStartBlock,
566
+ ~fromBlock,
567
+ ~maxBlockNumber,
568
+ ~targetBufferSize,
569
+ ) => {
570
+ let newItemsCounter = ref(0)
571
+ let latestOnBlockBlockNumber = ref(fromBlock)
572
+
573
+ // Simply iterate over every block
574
+ // could have a better algorithm to iterate over blocks in a more efficient way
575
+ // but raw loops are fast enough
576
+ while (
577
+ latestOnBlockBlockNumber.contents < maxBlockNumber &&
578
+ // Additional safeguard to prevent OOM
579
+ newItemsCounter.contents <= targetBufferSize
580
+ ) {
581
+ let blockNumber = latestOnBlockBlockNumber.contents + 1
582
+ latestOnBlockBlockNumber := blockNumber
583
+
584
+ for configIdx in 0 to onBlockConfigs->Array.length - 1 {
585
+ let onBlockConfig = onBlockConfigs->Array.getUnsafe(configIdx)
586
+
587
+ let handlerStartBlock = switch onBlockConfig.startBlock {
588
+ | Some(startBlock) => startBlock
589
+ | None => indexerStartBlock
590
+ }
591
+
592
+ if (
593
+ blockNumber >= handlerStartBlock &&
594
+ switch onBlockConfig.endBlock {
595
+ | Some(endBlock) => blockNumber <= endBlock
596
+ | None => true
597
+ } &&
598
+ (blockNumber - handlerStartBlock)->Pervasives.mod(onBlockConfig.interval) === 0
599
+ ) {
600
+ mutItems->Array.push(
601
+ Block({
602
+ onBlockConfig,
603
+ blockNumber,
604
+ logIndex: blockItemLogIndex + onBlockConfig.index,
605
+ }),
606
+ )
607
+ newItemsCounter := newItemsCounter.contents + 1
608
+ }
609
+ }
610
+ }
611
+
612
+ latestOnBlockBlockNumber.contents
613
+ }
614
+
558
615
  /*
559
616
  Update fetchState, merge registers and recompute derived values.
560
617
  Runs partition optimization when partitions change.
@@ -597,49 +654,14 @@ let updateInternal = (
597
654
  }
598
655
  mutItemsRef := Some(mutItems)
599
656
 
600
- let newItemsCounter = ref(0)
601
- let latestOnBlockBlockNumber = ref(fetchState.latestOnBlockBlockNumber)
602
-
603
- // Simply iterate over every block
604
- // could have a better algorithm to iterate over blocks in a more efficient way
605
- // but raw loops are fast enough
606
- while (
607
- latestOnBlockBlockNumber.contents < maxBlockNumber &&
608
- // Additional safeguard to prevent OOM
609
- newItemsCounter.contents <= fetchState.targetBufferSize
610
- ) {
611
- let blockNumber = latestOnBlockBlockNumber.contents + 1
612
- latestOnBlockBlockNumber := blockNumber
613
-
614
- for configIdx in 0 to onBlockConfigs->Array.length - 1 {
615
- let onBlockConfig = onBlockConfigs->Array.getUnsafe(configIdx)
616
-
617
- let handlerStartBlock = switch onBlockConfig.startBlock {
618
- | Some(startBlock) => startBlock
619
- | None => fetchState.startBlock
620
- }
621
-
622
- if (
623
- blockNumber >= handlerStartBlock &&
624
- switch onBlockConfig.endBlock {
625
- | Some(endBlock) => blockNumber <= endBlock
626
- | None => true
627
- } &&
628
- (blockNumber - handlerStartBlock)->Pervasives.mod(onBlockConfig.interval) === 0
629
- ) {
630
- mutItems->Array.push(
631
- Block({
632
- onBlockConfig,
633
- blockNumber,
634
- logIndex: blockItemLogIndex + onBlockConfig.index,
635
- }),
636
- )
637
- newItemsCounter := newItemsCounter.contents + 1
638
- }
639
- }
640
- }
641
-
642
- latestOnBlockBlockNumber.contents
657
+ appendOnBlockItems(
658
+ ~mutItems,
659
+ ~onBlockConfigs,
660
+ ~indexerStartBlock=fetchState.startBlock,
661
+ ~fromBlock=fetchState.latestOnBlockBlockNumber,
662
+ ~maxBlockNumber,
663
+ ~targetBufferSize=fetchState.targetBufferSize,
664
+ )
643
665
  }
644
666
  }
645
667
 
@@ -1661,35 +1683,59 @@ let make = (
1661
1683
  )
1662
1684
  }
1663
1685
 
1664
- let numAddresses = indexingAddresses->Utils.Dict.size
1665
- Prometheus.IndexingAddresses.set(~addressesCount=numAddresses, ~chainId)
1666
- Prometheus.IndexingPartitions.set(
1667
- ~partitionsCount=optimizedPartitions->OptimizedPartitions.count,
1668
- ~chainId,
1669
- )
1670
- Prometheus.IndexingBufferSize.set(~bufferSize=0, ~chainId)
1671
- Prometheus.IndexingBufferBlockNumber.set(~blockNumber=latestFetchedBlock.blockNumber, ~chainId)
1672
- switch endBlock {
1673
- | Some(endBlock) => Prometheus.IndexingEndBlock.set(~endBlock, ~chainId)
1674
- | None => ()
1686
+ // On resume knownHeight is restored from the DB but the buffer starts empty.
1687
+ // For onBlock-only indexers (e.g. SVM onSlot) there are no partitions to drive
1688
+ // fetching, so without seeding the buffer here getNextQuery would return
1689
+ // NothingToQuery and the indexer would get stuck.
1690
+ let buffer = []
1691
+ let latestOnBlockBlockNumber = if knownHeight > 0 && onBlockConfigs->Utils.Array.notEmpty {
1692
+ let maxBlockNumber = switch optimizedPartitions->OptimizedPartitions.getLatestFullyFetchedBlock {
1693
+ | None => knownHeight
1694
+ | Some(latestFullyFetchedBlock) => latestFullyFetchedBlock.blockNumber
1695
+ }
1696
+ appendOnBlockItems(
1697
+ ~mutItems=buffer,
1698
+ ~onBlockConfigs,
1699
+ ~indexerStartBlock=startBlock,
1700
+ ~fromBlock=progressBlockNumber,
1701
+ ~maxBlockNumber,
1702
+ ~targetBufferSize,
1703
+ )
1704
+ } else {
1705
+ progressBlockNumber
1675
1706
  }
1676
1707
 
1677
- {
1708
+ let fetchState = {
1678
1709
  optimizedPartitions,
1679
1710
  contractConfigs,
1680
1711
  chainId,
1681
1712
  startBlock,
1682
1713
  endBlock,
1683
- latestOnBlockBlockNumber: progressBlockNumber,
1714
+ latestOnBlockBlockNumber,
1684
1715
  normalSelection,
1685
1716
  indexingAddresses,
1686
1717
  blockLag,
1687
1718
  onBlockConfigs,
1688
1719
  targetBufferSize,
1689
1720
  knownHeight,
1690
- buffer: [],
1721
+ buffer,
1691
1722
  firstEventBlock,
1692
1723
  }
1724
+
1725
+ let numAddresses = indexingAddresses->Utils.Dict.size
1726
+ Prometheus.IndexingAddresses.set(~addressesCount=numAddresses, ~chainId)
1727
+ Prometheus.IndexingPartitions.set(
1728
+ ~partitionsCount=optimizedPartitions->OptimizedPartitions.count,
1729
+ ~chainId,
1730
+ )
1731
+ Prometheus.IndexingBufferSize.set(~bufferSize=buffer->Array.length, ~chainId)
1732
+ Prometheus.IndexingBufferBlockNumber.set(~blockNumber=fetchState->bufferBlockNumber, ~chainId)
1733
+ switch endBlock {
1734
+ | Some(endBlock) => Prometheus.IndexingEndBlock.set(~endBlock, ~chainId)
1735
+ | None => ()
1736
+ }
1737
+
1738
+ fetchState
1693
1739
  }
1694
1740
 
1695
1741
  let bufferSize = ({buffer}: t) => buffer->Array.length
@@ -450,6 +450,35 @@ function numAddresses(fetchState) {
450
450
  return Utils.Dict.size(fetchState.indexingAddresses);
451
451
  }
452
452
 
453
+ function appendOnBlockItems(mutItems, onBlockConfigs, indexerStartBlock, fromBlock, maxBlockNumber, targetBufferSize) {
454
+ let newItemsCounter = 0;
455
+ let latestOnBlockBlockNumber = fromBlock;
456
+ while (latestOnBlockBlockNumber < maxBlockNumber && newItemsCounter <= targetBufferSize) {
457
+ let blockNumber = latestOnBlockBlockNumber + 1 | 0;
458
+ latestOnBlockBlockNumber = blockNumber;
459
+ for (let configIdx = 0, configIdx_finish = onBlockConfigs.length; configIdx < configIdx_finish; ++configIdx) {
460
+ let onBlockConfig = onBlockConfigs[configIdx];
461
+ let startBlock = onBlockConfig.startBlock;
462
+ let handlerStartBlock = startBlock !== undefined ? startBlock : indexerStartBlock;
463
+ let tmp = false;
464
+ if (blockNumber >= handlerStartBlock) {
465
+ let endBlock = onBlockConfig.endBlock;
466
+ tmp = endBlock !== undefined ? blockNumber <= endBlock : true;
467
+ }
468
+ if (tmp && Primitive_int.mod_(blockNumber - handlerStartBlock | 0, onBlockConfig.interval) === 0) {
469
+ mutItems.push({
470
+ kind: 1,
471
+ onBlockConfig: onBlockConfig,
472
+ blockNumber: blockNumber,
473
+ logIndex: 16777216 + onBlockConfig.index | 0
474
+ });
475
+ newItemsCounter = newItemsCounter + 1 | 0;
476
+ }
477
+ }
478
+ };
479
+ return latestOnBlockBlockNumber;
480
+ }
481
+
453
482
  function updateInternal(fetchState, optimizedPartitionsOpt, indexingAddressesOpt, mutItems, blockLagOpt, knownHeightOpt) {
454
483
  let optimizedPartitions = optimizedPartitionsOpt !== undefined ? optimizedPartitionsOpt : fetchState.optimizedPartitions;
455
484
  let indexingAddresses = indexingAddressesOpt !== undefined ? indexingAddressesOpt : fetchState.indexingAddresses;
@@ -472,32 +501,7 @@ function updateInternal(fetchState, optimizedPartitionsOpt, indexingAddressesOpt
472
501
  let mutItems$2 = mutItemsRef;
473
502
  let mutItems$3 = mutItems$2 !== undefined ? mutItems$2 : fetchState.buffer.slice();
474
503
  mutItemsRef = mutItems$3;
475
- let newItemsCounter = 0;
476
- let latestOnBlockBlockNumber$1 = fetchState.latestOnBlockBlockNumber;
477
- while (latestOnBlockBlockNumber$1 < maxBlockNumber && newItemsCounter <= fetchState.targetBufferSize) {
478
- let blockNumber = latestOnBlockBlockNumber$1 + 1 | 0;
479
- latestOnBlockBlockNumber$1 = blockNumber;
480
- for (let configIdx = 0, configIdx_finish = onBlockConfigs.length; configIdx < configIdx_finish; ++configIdx) {
481
- let onBlockConfig = onBlockConfigs[configIdx];
482
- let startBlock = onBlockConfig.startBlock;
483
- let handlerStartBlock = startBlock !== undefined ? startBlock : fetchState.startBlock;
484
- let tmp = false;
485
- if (blockNumber >= handlerStartBlock) {
486
- let endBlock = onBlockConfig.endBlock;
487
- tmp = endBlock !== undefined ? blockNumber <= endBlock : true;
488
- }
489
- if (tmp && Primitive_int.mod_(blockNumber - handlerStartBlock | 0, onBlockConfig.interval) === 0) {
490
- mutItems$3.push({
491
- kind: 1,
492
- onBlockConfig: onBlockConfig,
493
- blockNumber: blockNumber,
494
- logIndex: 16777216 + onBlockConfig.index | 0
495
- });
496
- newItemsCounter = newItemsCounter + 1 | 0;
497
- }
498
- }
499
- };
500
- latestOnBlockBlockNumber = latestOnBlockBlockNumber$1;
504
+ latestOnBlockBlockNumber = appendOnBlockItems(mutItems$3, onBlockConfigs, fetchState.startBlock, fetchState.latestOnBlockBlockNumber, maxBlockNumber, fetchState.targetBufferSize);
501
505
  } else {
502
506
  latestOnBlockBlockNumber = knownHeight;
503
507
  }
@@ -1299,15 +1303,17 @@ function make$1(startBlock, endBlock, eventConfigs, addresses, maxAddrInPartitio
1299
1303
  if (optimizedPartitions.idsInAscOrder.length === 0 && Utils.$$Array.isEmpty(onBlockConfigs)) {
1300
1304
  Stdlib_JsError.throwWithMessage(`Invalid configuration: Nothing to fetch on chain ` + chainId.toString() + `. ` + (`addresses=` + addresses.length.toString() + `, `) + (`eventConfigs=` + eventConfigs.length.toString() + `, `) + (`normalEventConfigs=` + normalEventConfigs.length.toString() + `. `) + `Make sure that you provided at least one contract address to index, or have events with Wildcard mode enabled, or have onBlock handlers.`);
1301
1305
  }
1302
- let numAddresses = Utils.Dict.size(indexingAddresses);
1303
- Prometheus.IndexingAddresses.set(numAddresses, chainId);
1304
- Prometheus.IndexingPartitions.set(optimizedPartitions.idsInAscOrder.length, chainId);
1305
- Prometheus.IndexingBufferSize.set(0, chainId);
1306
- Prometheus.IndexingBufferBlockNumber.set(progressBlockNumber, chainId);
1307
- if (endBlock !== undefined) {
1308
- Prometheus.IndexingEndBlock.set(endBlock, chainId);
1306
+ let buffer = [];
1307
+ let latestOnBlockBlockNumber;
1308
+ if (knownHeight > 0 && Utils.$$Array.notEmpty(onBlockConfigs)) {
1309
+ let id = optimizedPartitions.idsInAscOrder[0];
1310
+ let latestFullyFetchedBlock = id !== undefined ? optimizedPartitions.entities[id].latestFetchedBlock : undefined;
1311
+ let maxBlockNumber = latestFullyFetchedBlock !== undefined ? latestFullyFetchedBlock.blockNumber : knownHeight;
1312
+ latestOnBlockBlockNumber = appendOnBlockItems(buffer, onBlockConfigs, startBlock, progressBlockNumber, maxBlockNumber, targetBufferSize);
1313
+ } else {
1314
+ latestOnBlockBlockNumber = progressBlockNumber;
1309
1315
  }
1310
- return {
1316
+ let fetchState = {
1311
1317
  optimizedPartitions: optimizedPartitions,
1312
1318
  startBlock: startBlock,
1313
1319
  endBlock: endBlock,
@@ -1315,14 +1321,23 @@ function make$1(startBlock, endBlock, eventConfigs, addresses, maxAddrInPartitio
1315
1321
  indexingAddresses: indexingAddresses,
1316
1322
  contractConfigs: contractConfigs,
1317
1323
  chainId: chainId,
1318
- latestOnBlockBlockNumber: progressBlockNumber,
1324
+ latestOnBlockBlockNumber: latestOnBlockBlockNumber,
1319
1325
  blockLag: blockLag,
1320
- buffer: [],
1326
+ buffer: buffer,
1321
1327
  targetBufferSize: targetBufferSize,
1322
1328
  onBlockConfigs: onBlockConfigs,
1323
1329
  knownHeight: knownHeight,
1324
1330
  firstEventBlock: firstEventBlock
1325
1331
  };
1332
+ let numAddresses = Utils.Dict.size(indexingAddresses);
1333
+ Prometheus.IndexingAddresses.set(numAddresses, chainId);
1334
+ Prometheus.IndexingPartitions.set(optimizedPartitions.idsInAscOrder.length, chainId);
1335
+ Prometheus.IndexingBufferSize.set(buffer.length, chainId);
1336
+ Prometheus.IndexingBufferBlockNumber.set(bufferBlockNumber(fetchState), chainId);
1337
+ if (endBlock !== undefined) {
1338
+ Prometheus.IndexingEndBlock.set(endBlock, chainId);
1339
+ }
1340
+ return fetchState;
1326
1341
  }
1327
1342
 
1328
1343
  function bufferSize(param) {
@@ -1607,6 +1622,7 @@ export {
1607
1622
  compareBufferItem,
1608
1623
  blockItemLogIndex,
1609
1624
  numAddresses,
1625
+ appendOnBlockItems,
1610
1626
  updateInternal,
1611
1627
  warnDifferentContractType,
1612
1628
  addressesByContractNameCount,
@@ -124,10 +124,19 @@ let getEffectInMemTable = (inMemoryStore: t, ~effect: Internal.effect) => {
124
124
  }
125
125
  }
126
126
 
127
- let getEffectOutput = (inMemTable: effectCacheInMemTable, key) =>
127
+ let hasEffectOutput = (inMemTable: effectCacheInMemTable, key) =>
128
128
  switch inMemTable.dict->Utils.Dict.dangerouslyGetNonOption(key) {
129
- | Some(Set({entity: output})) => Some(output)
130
- | Some(Delete(_)) | None => None
129
+ | Some(Set(_)) => true
130
+ | Some(Delete(_)) | None => false
131
+ }
132
+
133
+ // Returns the raw output. The output is itself an option for effects with an
134
+ // optional output, so it must never be wrapped in another option here: Some(None)
135
+ // is encoded as the nested-option sentinel and would leak to the handler.
136
+ let getEffectOutputUnsafe = (inMemTable: effectCacheInMemTable, key): Internal.effectOutput =>
137
+ switch inMemTable.dict->Utils.Dict.dangerouslyGetNonOption(key) {
138
+ | Some(Set({entity: output})) => output
139
+ | Some(Delete(_)) | None => %raw(`undefined`)
131
140
  }
132
141
 
133
142
  // Records a handler output. Persisted on the next write only when shouldCache;
@@ -90,11 +90,21 @@ function getEffectInMemTable(inMemoryStore, effect) {
90
90
  return table$1;
91
91
  }
92
92
 
93
- function getEffectOutput(inMemTable, key) {
93
+ function hasEffectOutput(inMemTable, key) {
94
+ let match = inMemTable.dict[key];
95
+ if (match !== undefined) {
96
+ return match.type === "SET";
97
+ } else {
98
+ return false;
99
+ }
100
+ }
101
+
102
+ function getEffectOutputUnsafe(inMemTable, key) {
94
103
  let match = inMemTable.dict[key];
95
104
  if (match !== undefined && match.type === "SET") {
96
- return Primitive_option.some(match.entity);
105
+ return match.entity;
97
106
  }
107
+ return undefined;
98
108
  }
99
109
 
100
110
  function setEffectOutput(inMemTable, checkpointId, cacheKey, output, shouldCache) {
@@ -487,7 +497,8 @@ export {
487
497
  make$1 as make,
488
498
  keepLatestChangesLimit,
489
499
  getEffectInMemTable,
490
- getEffectOutput,
500
+ hasEffectOutput,
501
+ getEffectOutputUnsafe,
491
502
  setEffectOutput,
492
503
  initEffectOutputFromDb,
493
504
  dropCommittedEffects,
package/src/LoadLayer.res CHANGED
@@ -336,8 +336,8 @@ let loadEffect = (
336
336
  ~load,
337
337
  ~shouldGroup,
338
338
  ~hasher=args => args.cacheKey,
339
- ~getUnsafeInMemory=hash => inMemTable->InMemoryStore.getEffectOutput(hash)->Option.getUnsafe,
340
- ~hasInMemory=hash => inMemTable->InMemoryStore.getEffectOutput(hash)->Option.isSome,
339
+ ~getUnsafeInMemory=hash => inMemTable->InMemoryStore.getEffectOutputUnsafe(hash),
340
+ ~hasInMemory=hash => inMemTable->InMemoryStore.hasEffectOutput(hash),
341
341
  ~input=effectArgs,
342
342
  )
343
343
  }
@@ -12,7 +12,6 @@ import * as TableIndices from "./TableIndices.res.mjs";
12
12
  import * as ErrorHandling from "./ErrorHandling.res.mjs";
13
13
  import * as InMemoryStore from "./InMemoryStore.res.mjs";
14
14
  import * as InMemoryTable from "./InMemoryTable.res.mjs";
15
- import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
16
15
  import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
17
16
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
18
17
 
@@ -187,7 +186,7 @@ function loadEffect(loadManager, persistence, effect, effectArgs, inMemoryStore,
187
186
  return await executeWithRateLimit(effect, argsToCall, inMemTable, onError, false);
188
187
  }
189
188
  };
190
- return LoadManager.call(loadManager, effectArgs, key, load, args => args.cacheKey, shouldGroup, hash => Stdlib_Option.isSome(InMemoryStore.getEffectOutput(inMemTable, hash)), hash => InMemoryStore.getEffectOutput(inMemTable, hash));
189
+ return LoadManager.call(loadManager, effectArgs, key, load, args => args.cacheKey, shouldGroup, hash => InMemoryStore.hasEffectOutput(inMemTable, hash), hash => InMemoryStore.getEffectOutputUnsafe(inMemTable, hash));
191
190
  }
192
191
 
193
192
  function loadByField(loadManager, persistence, operator, entityConfig, inMemoryStore, fieldName, fieldValueSchema, shouldGroup, item, fieldValue) {
@@ -41,25 +41,18 @@ let getSyncConfig = (
41
41
  let collectEventParams = (contracts: array<Internal.evmContractConfig>): array<
42
42
  HyperSyncClient.Decoder.eventParamsInput,
43
43
  > => {
44
- let seen = Dict.make()
45
44
  let result = []
46
45
  contracts->Array.forEach(contract => {
47
46
  contract.events->Array.forEach(event => {
48
- let key = event.sighash ++ "_" ++ event.topicCount->Int.toString
49
- switch seen->Dict.get(key) {
50
- | Some(_) => ()
51
- | None => {
52
- seen->Dict.set(key, true)
53
- result
54
- ->Array.push({
55
- HyperSyncClient.Decoder.sighash: event.sighash,
56
- topicCount: event.topicCount,
57
- eventName: event.name,
58
- params: event.paramsMetadata,
59
- })
60
- ->ignore
61
- }
62
- }
47
+ result
48
+ ->Array.push({
49
+ HyperSyncClient.Decoder.sighash: event.sighash,
50
+ topicCount: event.topicCount,
51
+ eventName: event.name,
52
+ contractName: contract.name,
53
+ params: event.paramsMetadata,
54
+ })
55
+ ->ignore
63
56
  })
64
57
  })
65
58
  result
@@ -22,24 +22,16 @@ function getSyncConfig(param) {
22
22
  }
23
23
 
24
24
  function collectEventParams(contracts) {
25
- let seen = {};
26
25
  let result = [];
27
26
  contracts.forEach(contract => {
28
27
  contract.events.forEach(event => {
29
- let key = event.sighash + "_" + event.topicCount.toString();
30
- let match = seen[key];
31
- if (match !== undefined) {
32
- return;
33
- } else {
34
- seen[key] = true;
35
- result.push({
36
- sighash: event.sighash,
37
- topicCount: event.topicCount,
38
- eventName: event.name,
39
- params: event.paramsMetadata
40
- });
41
- return;
42
- }
28
+ result.push({
29
+ sighash: event.sighash,
30
+ topicCount: event.topicCount,
31
+ eventName: event.name,
32
+ contractName: contract.name,
33
+ params: event.paramsMetadata
34
+ });
43
35
  });
44
36
  });
45
37
  return result;
@@ -322,11 +322,17 @@ module Decoder = {
322
322
  sighash: string,
323
323
  topicCount: int,
324
324
  eventName: string,
325
+ contractName: string,
325
326
  params: array<Internal.paramMeta>,
326
327
  }
327
328
 
329
+ // Decoded params keyed by contract name. Contracts that emit the same-signature
330
+ // event share one decode but get their own param names, so the caller picks the
331
+ // entry for the contract its router resolved the log to.
328
332
  type tWithParams = {
329
- decodeLogs: array<ResponseTypes.event> => promise<array<Nullable.t<Internal.eventParams>>>,
333
+ decodeLogs: array<ResponseTypes.event> => promise<
334
+ array<Nullable.t<dict<Internal.eventParams>>>,
335
+ >,
330
336
  }
331
337
 
332
338
  @send
@@ -348,7 +354,7 @@ module EventItems = {
348
354
  topicCount: int,
349
355
  block: ResponseTypes.block,
350
356
  transaction: ResponseTypes.transaction,
351
- params: Nullable.t<Internal.eventParams>,
357
+ params: Nullable.t<dict<Internal.eventParams>>,
352
358
  }
353
359
 
354
360
  type response = {
@@ -351,20 +351,23 @@ Learn more or get a free API token at: https://envio.dev/app/api-tokens`)
351
351
  ~blockNumber=item.block.number->Belt.Option.getUnsafe,
352
352
  )
353
353
 
354
- switch (maybeEventConfig, item.params) {
355
- | (Some(eventConfig), Value(decoded)) =>
356
- parsedQueueItems
357
- ->Array.push(makeEventBatchQueueItem(item, ~params=decoded, ~eventConfig))
358
- ->ignore
359
- | (Some(eventConfig), Null | Undefined) =>
360
- handleDecodeFailure(
361
- ~eventConfig,
362
- ~logIndex=item.logIndex,
363
- ~blockNumber=item.block.number->Belt.Option.getUnsafe,
364
- ~chainId,
365
- ~exn=UndefinedValue,
366
- )
367
- | (None, _) => () //ignore events that aren't registered
354
+ switch maybeEventConfig {
355
+ | None => () //ignore events that aren't registered
356
+ | Some(eventConfig) =>
357
+ switch item.params
358
+ ->Nullable.toOption
359
+ ->Option.flatMap(Dict.get(_, eventConfig.contractName)) {
360
+ | Some(params) =>
361
+ parsedQueueItems->Array.push(makeEventBatchQueueItem(item, ~params, ~eventConfig))->ignore
362
+ | None =>
363
+ handleDecodeFailure(
364
+ ~eventConfig,
365
+ ~logIndex=item.logIndex,
366
+ ~blockNumber=item.block.number->Belt.Option.getUnsafe,
367
+ ~chainId,
368
+ ~exn=UndefinedValue,
369
+ )
370
+ }
368
371
  }
369
372
  })
370
373
 
@@ -14,6 +14,7 @@ import * as ErrorHandling from "../ErrorHandling.res.mjs";
14
14
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
15
15
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
16
16
  import * as HyperSyncClient from "./HyperSyncClient.res.mjs";
17
+ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
17
18
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
18
19
  import * as HyperSyncHeightStream from "./HyperSyncHeightStream.res.mjs";
19
20
 
@@ -215,32 +216,31 @@ Learn more or get a free API token at: https://envio.dev/app/api-tokens`);
215
216
  let parsedQueueItems = [];
216
217
  Belt_Array.forEach(pageUnsafe.items, item => {
217
218
  let maybeEventConfig = EventRouter.get(eventRouter, EventRouter.getEvmEventId(item.topic0, item.topicCount), item.srcAddress, item.block.number, indexingAddresses);
218
- let match = item.params;
219
219
  if (maybeEventConfig === undefined) {
220
220
  return;
221
221
  }
222
- if (match == null) {
223
- match === null;
224
- } else {
225
- parsedQueueItems.push(makeEventBatchQueueItem(item, match, maybeEventConfig));
226
- return;
227
- }
228
- let logIndex = item.logIndex;
229
- let blockNumber = item.block.number;
230
- let exn = {
231
- RE_EXN_ID: UndefinedValue
232
- };
233
- if (maybeEventConfig.isWildcard) {
222
+ let params = Stdlib_Option.flatMap(Primitive_option.fromNullable(item.params), __x => __x[maybeEventConfig.contractName]);
223
+ if (params !== undefined) {
224
+ parsedQueueItems.push(makeEventBatchQueueItem(item, Primitive_option.valFromOption(params), maybeEventConfig));
234
225
  return;
226
+ } else {
227
+ let logIndex = item.logIndex;
228
+ let blockNumber = item.block.number;
229
+ let exn = {
230
+ RE_EXN_ID: UndefinedValue
231
+ };
232
+ if (maybeEventConfig.isWildcard) {
233
+ return;
234
+ }
235
+ let msg = `Event ` + maybeEventConfig.name + ` was unexpectedly parsed as undefined`;
236
+ let logger$1 = Logging.createChildFrom(logger, {
237
+ chainId: chain,
238
+ blockNumber: blockNumber,
239
+ logIndex: logIndex,
240
+ decoder: "hypersync-client"
241
+ });
242
+ return ErrorHandling.mkLogAndRaise(logger$1, msg, exn);
235
243
  }
236
- let msg = `Event ` + maybeEventConfig.name + ` was unexpectedly parsed as undefined`;
237
- let logger$1 = Logging.createChildFrom(logger, {
238
- chainId: chain,
239
- blockNumber: blockNumber,
240
- logIndex: logIndex,
241
- decoder: "hypersync-client"
242
- });
243
- ErrorHandling.mkLogAndRaise(logger$1, msg, exn);
244
244
  });
245
245
  let parsingTimeElapsed = Hrtime.toSecondsFloat(Hrtime.timeSince(parsingTimeRef));
246
246
  let blockHashes = [];
@@ -1115,7 +1115,7 @@ let make = (
1115
1115
  ->Array.zip(parsedEvents)
1116
1116
  ->Array.filterMap(((
1117
1117
  log: Rpc.GetLogs.log,
1118
- maybeDecodedEvent: Nullable.t<Internal.eventParams>,
1118
+ maybeDecodedEvent: Nullable.t<dict<Internal.eventParams>>,
1119
1119
  )) => {
1120
1120
  let topic0 = log.topics[0]->Option.getOr("0x0")
1121
1121
  let routedAddress = if lowercaseAddresses {
@@ -1132,8 +1132,10 @@ let make = (
1132
1132
  ) {
1133
1133
  | None => None
1134
1134
  | Some(eventConfig) =>
1135
- switch maybeDecodedEvent {
1136
- | Value(decoded) =>
1135
+ switch maybeDecodedEvent
1136
+ ->Nullable.toOption
1137
+ ->Option.flatMap(Dict.get(_, eventConfig.contractName)) {
1138
+ | Some(decoded) =>
1137
1139
  Some(
1138
1140
  (
1139
1141
  async () => {
@@ -1176,7 +1178,7 @@ let make = (
1176
1178
  }
1177
1179
  )(),
1178
1180
  )
1179
- | Null | Undefined => None
1181
+ | None => None
1180
1182
  }
1181
1183
  }
1182
1184
  })
@@ -1064,54 +1064,59 @@ function make(param) {
1064
1064
  };
1065
1065
  }
1066
1066
  let parsedQueueItems = await Promise.all(Stdlib_Array.filterMap(Stdlib_Array.zip(logs, parsedEvents), param => {
1067
- let maybeDecodedEvent = param[1];
1068
1067
  let log = param[0];
1069
1068
  let topic0 = Stdlib_Option.getOr(log.topics[0], "0x0");
1070
1069
  let routedAddress = lowercaseAddresses ? Address.Evm.fromAddressLowercaseOrThrow(log.address) : Address.Evm.fromAddressOrThrow(log.address);
1071
1070
  let eventConfig = EventRouter.get(eventRouter, EventRouter.getEvmEventId(topic0, log.topics.length), routedAddress, log.blockNumber, indexingAddresses);
1072
- if (eventConfig !== undefined && maybeDecodedEvent !== null && maybeDecodedEvent !== undefined) {
1073
- return (async () => {
1074
- let match;
1075
- try {
1076
- match = await Promise.all([
1077
- getEventBlockOrThrow(log, eventConfig.selectedBlockFields),
1078
- getEventTransactionOrThrow(log, eventConfig.selectedTransactionFields)
1079
- ]);
1080
- } catch (raw_exn) {
1081
- let exn = Primitive_exceptions.internalToException(raw_exn);
1082
- throw {
1083
- RE_EXN_ID: Source.GetItemsError,
1084
- _1: {
1085
- TAG: "FailedGettingFieldSelection",
1086
- exn: exn,
1087
- blockNumber: log.blockNumber,
1088
- logIndex: log.logIndex,
1089
- message: "Failed getting selected fields. Please double-check your RPC provider returns correct data."
1090
- },
1091
- Error: new Error()
1092
- };
1093
- }
1094
- let block = match[0];
1095
- return {
1096
- kind: 0,
1097
- eventConfig: eventConfig,
1098
- timestamp: block.timestamp,
1099
- chain: chain,
1100
- blockNumber: block.number,
1101
- logIndex: log.logIndex,
1102
- event: {
1103
- contractName: eventConfig.contractName,
1104
- eventName: eventConfig.name,
1105
- params: maybeDecodedEvent,
1106
- chainId: chain,
1107
- srcAddress: routedAddress,
1071
+ if (eventConfig === undefined) {
1072
+ return;
1073
+ }
1074
+ let decoded = Stdlib_Option.flatMap(Primitive_option.fromNullable(param[1]), __x => __x[eventConfig.contractName]);
1075
+ if (decoded === undefined) {
1076
+ return;
1077
+ }
1078
+ let decoded$1 = Primitive_option.valFromOption(decoded);
1079
+ return (async () => {
1080
+ let match;
1081
+ try {
1082
+ match = await Promise.all([
1083
+ getEventBlockOrThrow(log, eventConfig.selectedBlockFields),
1084
+ getEventTransactionOrThrow(log, eventConfig.selectedTransactionFields)
1085
+ ]);
1086
+ } catch (raw_exn) {
1087
+ let exn = Primitive_exceptions.internalToException(raw_exn);
1088
+ throw {
1089
+ RE_EXN_ID: Source.GetItemsError,
1090
+ _1: {
1091
+ TAG: "FailedGettingFieldSelection",
1092
+ exn: exn,
1093
+ blockNumber: log.blockNumber,
1108
1094
  logIndex: log.logIndex,
1109
- transaction: match[1],
1110
- block: block
1111
- }
1095
+ message: "Failed getting selected fields. Please double-check your RPC provider returns correct data."
1096
+ },
1097
+ Error: new Error()
1112
1098
  };
1113
- })();
1114
- }
1099
+ }
1100
+ let block = match[0];
1101
+ return {
1102
+ kind: 0,
1103
+ eventConfig: eventConfig,
1104
+ timestamp: block.timestamp,
1105
+ chain: chain,
1106
+ blockNumber: block.number,
1107
+ logIndex: log.logIndex,
1108
+ event: {
1109
+ contractName: eventConfig.contractName,
1110
+ eventName: eventConfig.name,
1111
+ params: decoded$1,
1112
+ chainId: chain,
1113
+ srcAddress: routedAddress,
1114
+ logIndex: log.logIndex,
1115
+ transaction: match[1],
1116
+ block: block
1117
+ }
1118
+ };
1119
+ })();
1115
1120
  }));
1116
1121
  let optFirstBlockParent = await firstBlockParentPromise;
1117
1122
  let totalTimeElapsed = Hrtime.toSecondsFloat(Hrtime.timeSince(startFetchingBatchTimeRef));