envio 3.1.2 → 3.2.1

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 +87 -0
  19. package/src/EventConfigBuilder.res.mjs +53 -0
  20. package/src/EventUtils.res +2 -2
  21. package/src/FetchState.res +63 -67
  22. package/src/FetchState.res.mjs +44 -42
  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 +18 -2
  29. package/src/HandlerLoader.res.mjs +16 -34
  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 +70 -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 +90 -21
  46. package/src/LogSelection.res.mjs +72 -21
  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 +35 -68
  68. package/src/TestIndexer.res.mjs +17 -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 +12 -11
  114. package/src/sources/HyperSyncSource.res.mjs +6 -6
  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
package/src/Config.res CHANGED
@@ -28,7 +28,7 @@ type evmRpcConfig = {
28
28
  type sourceConfig =
29
29
  | EvmSourceConfig({hypersync: option<string>, rpcs: array<evmRpcConfig>})
30
30
  | FuelSourceConfig({hypersync: string})
31
- | SvmSourceConfig({rpc: string})
31
+ | SvmSourceConfig({hypersync: option<string>, rpc: option<string>})
32
32
  // For tests: pass custom sources directly
33
33
  | CustomSources(array<Source.t>)
34
34
 
@@ -91,7 +91,7 @@ module EnvioAddresses = {
91
91
  let index = -1
92
92
 
93
93
  let makeId = (~chainId, ~address) => {
94
- chainId->Belt.Int.toString ++ "-" ++ address->Address.toString
94
+ chainId->Int.toString ++ "-" ++ address->Address.toString
95
95
  }
96
96
 
97
97
  type t = {
@@ -121,8 +121,6 @@ module EnvioAddresses = {
121
121
  contractName: s.matches(S.string),
122
122
  })
123
123
 
124
- let rowsSchema = S.array(schema)
125
-
126
124
  let table = Table.mkTable(
127
125
  name,
128
126
  ~fields=[
@@ -141,7 +139,6 @@ module EnvioAddresses = {
141
139
  Internal.name,
142
140
  index,
143
141
  schema,
144
- rowsSchema,
145
142
  table,
146
143
  // Internal address tracking is Postgres-only; the global config is
147
144
  // always required to have Postgres enabled (Storage::resolve forbids
@@ -194,6 +191,39 @@ let publicConfigChainSchema = S.schema(s =>
194
191
  }
195
192
  )
196
193
 
194
+ let svmEventDescriptorSchema = S.schema(s =>
195
+ {
196
+ "discriminator": s.matches(S.option(S.string)),
197
+ "discriminatorByteLen": s.matches(S.int),
198
+ "includeTransaction": s.matches(S.bool),
199
+ "includeLogs": s.matches(S.bool),
200
+ "includeTokenBalances": s.matches(S.bool),
201
+ "accountFilters": s.matches(
202
+ S.option(
203
+ S.array(
204
+ S.schema(s =>
205
+ {
206
+ "position": s.matches(S.int),
207
+ "values": s.matches(S.array(S.string)),
208
+ }
209
+ ),
210
+ ),
211
+ ),
212
+ ),
213
+ "isInner": s.matches(S.option(S.bool)),
214
+ "accounts": s.matches(S.option(S.array(S.string))),
215
+ "args": s.matches(S.option(S.json(~validate=false))),
216
+ }
217
+ )
218
+
219
+ let svmAbiSchema = S.schema(s =>
220
+ {
221
+ "programId": s.matches(S.string),
222
+ "definedTypes": s.matches(S.json(~validate=false)),
223
+ "source": s.matches(S.string),
224
+ }
225
+ )
226
+
197
227
  let contractEventItemSchema = S.schema(s =>
198
228
  {
199
229
  "name": s.matches(S.string),
@@ -202,6 +232,7 @@ let contractEventItemSchema = S.schema(s =>
202
232
  "kind": s.matches(S.option(S.string)),
203
233
  "blockFields": s.matches(S.option(S.array(Internal.evmBlockFieldSchema))),
204
234
  "transactionFields": s.matches(S.option(S.array(Internal.evmTransactionFieldSchema))),
235
+ "svm": s.matches(S.option(svmEventDescriptorSchema)),
205
236
  }
206
237
  )
207
238
 
@@ -211,6 +242,8 @@ let contractConfigSchema = S.schema(s =>
211
242
  "handler": s.matches(S.option(S.string)),
212
243
  // EVM-specific: event signatures for HyperSync queries
213
244
  "events": s.matches(S.option(S.array(contractEventItemSchema))),
245
+ // SVM-only: program-level Borsh schema (defined-types registry, source).
246
+ "svmAbi": s.matches(S.option(svmAbiSchema)),
214
247
  }
215
248
  )
216
249
 
@@ -218,6 +251,10 @@ let publicConfigEcosystemSchema = S.schema(s =>
218
251
  {
219
252
  "chains": s.matches(S.dict(publicConfigChainSchema)),
220
253
  "contracts": s.matches(S.option(S.dict(contractConfigSchema))),
254
+ // SVM-only alias: programs are the SVM analog of EVM/Fuel contracts.
255
+ // Parsed via the same `contractConfigSchema` and read in `fromPublic`'s
256
+ // `publicContractsConfig` switch.
257
+ "programs": s.matches(S.option(S.dict(contractConfigSchema))),
221
258
  }
222
259
  )
223
260
 
@@ -252,6 +289,8 @@ let derivedFieldSchema = S.schema(s =>
252
289
  let propertySchema = S.schema(s =>
253
290
  {
254
291
  "name": s.matches(S.string),
292
+ "postgresDbName": s.matches(S.option(S.string)),
293
+ "clickhouseDbName": s.matches(S.option(S.string)),
255
294
  "type": s.matches(S.string),
256
295
  "isNullable": s.matches(S.option(S.bool)),
257
296
  "isArray": s.matches(S.option(S.bool)),
@@ -373,6 +412,8 @@ let parseEntitiesFromJson = (
373
412
  ~isIndex,
374
413
  ~linkedEntity=?prop["linkedEntity"],
375
414
  ~description=?prop["description"],
415
+ ~postgresDbName=?prop["postgresDbName"],
416
+ ~clickhouseDbName=?prop["clickhouseDbName"],
376
417
  )
377
418
  })
378
419
 
@@ -407,19 +448,21 @@ let parseEntitiesFromJson = (
407
448
  ~description=?entityJson["description"],
408
449
  )
409
450
 
451
+ let getApiFieldName = prop =>
452
+ switch prop["linkedEntity"] {
453
+ | Some(_) => prop["name"] ++ "_id"
454
+ | None => prop["name"]
455
+ }
456
+
410
457
  // Build schema dynamically from properties
411
- // Use db field names (with _id suffix for linked entities) as schema locations
412
- // to match the database column names used in Table.toSqlParams
458
+ // Use API field names (with _id suffix for linked entities) as schema
459
+ // locations to match the generated entity types
413
460
  let schema = S.schema(s => {
414
461
  let dict = Dict.make()
415
462
  entityJson["properties"]->Array.forEach(
416
463
  prop => {
417
464
  let (_, fieldSchema, _, _, _) = getFieldTypeAndSchema(prop, ~enumConfigsByName)
418
- let dbFieldName = switch prop["linkedEntity"] {
419
- | Some(_) => prop["name"] ++ "_id"
420
- | None => prop["name"]
421
- }
422
- dict->Dict.set(dbFieldName, s.matches(fieldSchema))
465
+ dict->Dict.set(prop->getApiFieldName, s.matches(fieldSchema))
423
466
  },
424
467
  )
425
468
  dict
@@ -444,9 +487,6 @@ let parseEntitiesFromJson = (
444
487
  Internal.name: entityName,
445
488
  index,
446
489
  schema: schema->(Utils.magic: S.t<dict<unknown>> => S.t<Internal.entity>),
447
- rowsSchema: S.array(schema)->(
448
- Utils.magic: S.t<array<dict<unknown>>> => S.t<array<Internal.entity>>
449
- ),
450
490
  table,
451
491
  storage,
452
492
  }->Internal.fromGenericEntityConfig
@@ -518,10 +558,19 @@ let fromPublic = (publicConfigJson: JSON.t) => {
518
558
  | None => false
519
559
  }
520
560
 
521
- // Parse contract configs (ABIs, events, handlers)
522
- let publicContractsConfig = switch (ecosystemName, publicConfig["evm"], publicConfig["fuel"]) {
523
- | (Ecosystem.Evm, Some(evm), _) => evm["contracts"]
524
- | (Ecosystem.Fuel, _, Some(fuel)) => fuel["contracts"]
561
+ // Parse contract configs (ABIs, events, handlers).
562
+ // SVM stores them under `svm.programs` in the public JSON — the per-program
563
+ // events drive `indexer.onInstruction` registration the same way EVM/Fuel
564
+ // contracts drive `onEvent`.
565
+ let publicContractsConfig = switch (
566
+ ecosystemName,
567
+ publicConfig["evm"],
568
+ publicConfig["fuel"],
569
+ publicConfig["svm"],
570
+ ) {
571
+ | (Ecosystem.Evm, Some(evm), _, _) => evm["contracts"]
572
+ | (Ecosystem.Fuel, _, Some(fuel), _) => fuel["contracts"]
573
+ | (Ecosystem.Svm, _, _, Some(svm)) => svm["programs"]
525
574
  | _ => None
526
575
  }
527
576
 
@@ -539,7 +588,16 @@ let fromPublic = (publicConfigJson: JSON.t) => {
539
588
  | None => (Utils.Set.fromArray(EventConfigBuilder.alwaysIncludedBlockFields), Utils.Set.make())
540
589
  }
541
590
 
542
- let contractDataByName: dict<{"abi": EvmTypes.Abi.t, "events": option<array<_>>}> = Dict.make()
591
+ let contractDataByName: dict<{
592
+ "abi": EvmTypes.Abi.t,
593
+ "eventSignatures": array<string>,
594
+ "events": option<array<_>>,
595
+ "svmAbi": option<{
596
+ "programId": string,
597
+ "definedTypes": JSON.t,
598
+ "source": string,
599
+ }>,
600
+ }> = Dict.make()
543
601
  switch publicContractsConfig {
544
602
  | Some(contractsDict) =>
545
603
  contractsDict
@@ -547,21 +605,48 @@ let fromPublic = (publicConfigJson: JSON.t) => {
547
605
  ->Array.forEach(((contractName, contractConfig)) => {
548
606
  let capitalizedName = contractName->Utils.String.capitalize
549
607
  let abi = contractConfig["abi"]->(Utils.magic: JSON.t => EvmTypes.Abi.t)
608
+ let eventSignatures = switch contractConfig["events"] {
609
+ | Some(events) => events->Array.map(eventItem => eventItem["sighash"])
610
+ | None => []
611
+ }
612
+ let widened =
613
+ contractConfig->(
614
+ Utils.magic: _ => {
615
+ "svmAbi": option<{
616
+ "programId": string,
617
+ "definedTypes": JSON.t,
618
+ "source": string,
619
+ }>,
620
+ }
621
+ )
550
622
  contractDataByName->Dict.set(
551
623
  capitalizedName,
552
- {"abi": abi, "events": contractConfig["events"]},
624
+ {
625
+ "abi": abi,
626
+ "eventSignatures": eventSignatures,
627
+ "events": contractConfig["events"],
628
+ "svmAbi": widened["svmAbi"],
629
+ },
553
630
  )
554
631
  })
555
632
  | None => ()
556
633
  }
557
634
 
558
- // Build event configs for a contract from JSON event items
635
+ // Build event configs for a contract from JSON event items.
636
+ //
637
+ // `~addresses` is the chain-side address list. For SVM programs it's the
638
+ // single base58 program_id — wired onto each instruction's event config so
639
+ // the source can build `(programId, discriminator)`-keyed InstructionSelections.
640
+ // EVM and Fuel ignore it (the address lives in `ChainContract.addresses` and
641
+ // is looked up at dispatch time, not stamped on the event).
559
642
  let buildContractEvents = (
560
643
  ~contractName,
561
644
  ~events: option<array<_>>,
562
645
  ~abi,
563
646
  ~chainId: int,
564
647
  ~startBlock: option<int>,
648
+ ~addresses: array<string>,
649
+ ~svmDefinedTypes: JSON.t=JSON.Null,
565
650
  ) => {
566
651
  switch events {
567
652
  | None => []
@@ -592,6 +677,73 @@ let fromPublic = (publicConfigJson: JSON.t) => {
592
677
  `Fuel event ${contractName}.${eventName} is missing "kind" in internal config`,
593
678
  )
594
679
  }
680
+ | Ecosystem.Svm =>
681
+ let programId = switch addresses {
682
+ | [pid] => pid->SvmTypes.Pubkey.fromStringUnsafe
683
+ | [] =>
684
+ JsError.throwWithMessage(
685
+ `SVM program ${contractName} on chain ${chainId->Int.toString} is missing a program_id`,
686
+ )
687
+ | _ =>
688
+ JsError.throwWithMessage(
689
+ `SVM program ${contractName} on chain ${chainId->Int.toString} has multiple addresses; a program is uniquely identified by a single program_id`,
690
+ )
691
+ }
692
+ let widenedEventItem =
693
+ eventItem->(
694
+ Utils.magic: _ => {
695
+ "svm": option<{
696
+ "discriminator": option<string>,
697
+ "discriminatorByteLen": int,
698
+ "includeTransaction": bool,
699
+ "includeLogs": bool,
700
+ "includeTokenBalances": bool,
701
+ "accountFilters": option<
702
+ array<array<{"position": int, "values": array<string>}>>,
703
+ >,
704
+ "isInner": option<bool>,
705
+ "accounts": option<array<string>>,
706
+ "args": option<JSON.t>,
707
+ }>,
708
+ }
709
+ )
710
+ let svm = switch widenedEventItem["svm"] {
711
+ | Some(s) => s
712
+ | None =>
713
+ JsError.throwWithMessage(
714
+ `SVM instruction ${contractName}.${eventName} is missing the "svm" descriptor in internal config`,
715
+ )
716
+ }
717
+ let accountFilters =
718
+ svm["accountFilters"]
719
+ ->Option.getOr([])
720
+ ->Array.map(group =>
721
+ group->Array.map(
722
+ af => {
723
+ Internal.position: af["position"],
724
+ values: af["values"]->SvmTypes.Pubkey.fromStringsUnsafe,
725
+ },
726
+ )
727
+ )
728
+ (EventConfigBuilder.buildSvmInstructionEventConfig(
729
+ ~contractName,
730
+ ~instructionName=eventName,
731
+ ~programId,
732
+ ~discriminator=svm["discriminator"],
733
+ ~discriminatorByteLen=svm["discriminatorByteLen"],
734
+ ~includeTransaction=svm["includeTransaction"],
735
+ ~includeLogs=svm["includeLogs"],
736
+ ~includeTokenBalances=svm["includeTokenBalances"],
737
+ ~accountFilters,
738
+ ~isInner=svm["isInner"],
739
+ ~isWildcard=false,
740
+ ~handler=None,
741
+ ~contractRegister=None,
742
+ ~accounts=svm["accounts"]->Option.getOr([]),
743
+ ~args=svm["args"]->Option.getOr(JSON.Null),
744
+ ~definedTypes=svmDefinedTypes,
745
+ ~startBlock?,
746
+ ) :> Internal.eventConfig)
595
747
  | _ =>
596
748
  (EventConfigBuilder.buildEvmEventConfig(
597
749
  ~contractName,
@@ -651,11 +803,11 @@ let fromPublic = (publicConfigJson: JSON.t) => {
651
803
  ->Dict.toArray
652
804
  ->Array.map(((capitalizedName, contractData)) => {
653
805
  let chainContract = chainContracts->Dict.get(capitalizedName)
654
- let addresses =
806
+ let rawAddresses =
655
807
  chainContract
656
808
  ->Option.flatMap(cc => cc["addresses"])
657
809
  ->Option.getOr([])
658
- ->Array.map(parseAddress)
810
+ let addresses = rawAddresses->Array.map(parseAddress)
659
811
  let startBlock = chainContract->Option.flatMap(cc => cc["startBlock"])
660
812
 
661
813
  // Build event configs from JSON (field selections resolved inline)
@@ -669,6 +821,10 @@ let fromPublic = (publicConfigJson: JSON.t) => {
669
821
  ~abi=contractData["abi"],
670
822
  ~chainId,
671
823
  ~startBlock,
824
+ ~addresses=rawAddresses,
825
+ ~svmDefinedTypes=contractData["svmAbi"]
826
+ ->Option.map(a => a["definedTypes"])
827
+ ->Option.getOr(JSON.Null),
672
828
  )
673
829
 
674
830
  {
@@ -680,6 +836,29 @@ let fromPublic = (publicConfigJson: JSON.t) => {
680
836
  }
681
837
  })
682
838
 
839
+ // The same address under two contract definitions (or twice under one)
840
+ // would later violate the (chainId, address) primary key of
841
+ // envio_addresses with an opaque Postgres error — fail fast with the
842
+ // offending pair instead. parseAddress already canonicalizes casing
843
+ // (checksum or lowercase), so an exact match catches case variants too.
844
+ let contractNameByAddress = Dict.make()
845
+ contracts->Array.forEach(contract => {
846
+ contract.addresses->Array.forEach(
847
+ address => {
848
+ let addressString = address->Address.toString
849
+ switch contractNameByAddress->Dict.get(addressString) {
850
+ | Some(existingContractName) =>
851
+ JsError.throwWithMessage(
852
+ existingContractName === contract.name
853
+ ? `Address ${addressString} is listed multiple times for the contract ${contract.name} on chain ${chainId->Int.toString}. Please remove the duplicate from your config.`
854
+ : `Address ${addressString} on chain ${chainId->Int.toString} is configured for multiple contracts: ${existingContractName} and ${contract.name}. Indexing the same address with multiple contract definitions is not supported. Please define the events on a single contract definition instead.`,
855
+ )
856
+ | None => contractNameByAddress->Dict.set(addressString, contract.name)
857
+ }
858
+ },
859
+ )
860
+ })
861
+
683
862
  let sourceConfig = switch ecosystemName {
684
863
  | Ecosystem.Evm =>
685
864
  let rpcs =
@@ -732,10 +911,14 @@ let fromPublic = (publicConfigJson: JSON.t) => {
732
911
  JsError.throwWithMessage(`Chain ${chainName} is missing hypersync endpoint in config`)
733
912
  }
734
913
  | Ecosystem.Svm =>
735
- switch publicChainConfig["rpc"] {
736
- | Some(rpc) => SvmSourceConfig({rpc: rpc})
737
- | None => JsError.throwWithMessage(`Chain ${chainName} is missing rpc endpoint in config`)
914
+ let hypersync = publicChainConfig["hypersync"]
915
+ let rpc = publicChainConfig["rpc"]
916
+ if hypersync->Option.isNone && rpc->Option.isNone {
917
+ JsError.throwWithMessage(
918
+ `Chain ${chainName} is missing a data source: provide either an rpc endpoint or an experimental hypersync config`,
919
+ )
738
920
  }
921
+ SvmSourceConfig({hypersync, rpc})
739
922
  }
740
923
 
741
924
  {
@@ -849,7 +1032,7 @@ let getEventConfig = (config: t, ~contractName, ~eventName, ~chainId: option<int
849
1032
  | None =>
850
1033
  chain.contracts
851
1034
  ->Array.find(c => c.name == contractName)
852
- ->Belt.Option.flatMap(contract => contract.events->Array.find(e => e.name == eventName))
1035
+ ->Option.flatMap(contract => contract.events->Array.find(e => e.name == eventName))
853
1036
  }
854
1037
  })
855
1038
  }
@@ -973,7 +1156,7 @@ let diffPaths = (~stored: JSON.t, ~current: JSON.t): array<string> => {
973
1156
  let maxLen = Math.Int.max(sArr->Array.length, cArr->Array.length)
974
1157
  for i in 0 to maxLen - 1 {
975
1158
  let p = `${prefix}[${Int.toString(i)}]`
976
- switch (sArr->Belt.Array.get(i), cArr->Belt.Array.get(i)) {
1159
+ switch (sArr->Array.get(i), cArr->Array.get(i)) {
977
1160
  | (None, _) | (_, None) => acc->Array.push(p)->ignore
978
1161
  | (Some(sv), Some(cv)) => go(sv, cv, p)
979
1162
  }
@@ -1019,7 +1202,7 @@ let diffPaths = (~stored: JSON.t, ~current: JSON.t): array<string> => {
1019
1202
  switch firstHit {
1020
1203
  | Some(hits) => runTier(hits)
1021
1204
  | None =>
1022
- let knownSet = Utils.Set.fromArray(tiers->Belt.Array.concatMany)
1205
+ let knownSet = Utils.Set.fromArray(tiers->Array.flat)
1023
1206
  let extras =
1024
1207
  Utils.Set.fromArray(Array.concat(sObj->Dict.keysToArray, cObj->Dict.keysToArray))
1025
1208
  ->Utils.Set.toArray