envio 3.7.0 → 3.9.0-resolvers.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/README.md +2 -2
  2. package/evm.schema.json +7 -0
  3. package/fuel.schema.json +7 -0
  4. package/index.d.ts +674 -161
  5. package/index.js +10 -0
  6. package/package.json +6 -7
  7. package/src/AddressRows.res +121 -0
  8. package/src/AddressRows.res.mjs +143 -0
  9. package/src/Batch.res +11 -0
  10. package/src/Batch.res.mjs +2 -1
  11. package/src/Bin.res +47 -0
  12. package/src/Bin.res.mjs +34 -1
  13. package/src/ChainState.res +108 -98
  14. package/src/ChainState.res.mjs +86 -67
  15. package/src/ChainState.resi +28 -15
  16. package/src/Config.res +61 -131
  17. package/src/Config.res.mjs +47 -79
  18. package/src/ContractMapping.res +66 -0
  19. package/src/ContractMapping.res.mjs +73 -0
  20. package/src/Core.res +24 -0
  21. package/src/Core.res.mjs +4 -0
  22. package/src/Ecosystem.res +7 -2
  23. package/src/Env.res +128 -1
  24. package/src/Env.res.mjs +123 -1
  25. package/src/Envio.res +54 -76
  26. package/src/EventConfigBuilder.res +316 -46
  27. package/src/EventConfigBuilder.res.mjs +231 -28
  28. package/src/FetchState.res +59 -67
  29. package/src/FetchState.res.mjs +34 -44
  30. package/src/HandlerRegister.res +26 -15
  31. package/src/HandlerRegister.res.mjs +27 -7
  32. package/src/Hasura.res +30 -22
  33. package/src/Hasura.res.mjs +11 -5
  34. package/src/InMemoryStore.res +23 -38
  35. package/src/InMemoryStore.res.mjs +13 -27
  36. package/src/IndexerState.res +40 -3
  37. package/src/IndexerState.res.mjs +30 -4
  38. package/src/IndexerState.resi +4 -1
  39. package/src/Internal.res +49 -30
  40. package/src/Internal.res.mjs +12 -16
  41. package/src/Main.res +27 -20
  42. package/src/Main.res.mjs +19 -14
  43. package/src/Metrics.res +15 -1
  44. package/src/Metrics.res.mjs +5 -1
  45. package/src/Persistence.res +46 -21
  46. package/src/Persistence.res.mjs +8 -5
  47. package/src/PgStorage.res +238 -79
  48. package/src/PgStorage.res.mjs +115 -73
  49. package/src/PruneStaleHistory.res +1 -1
  50. package/src/PruneStaleHistory.res.mjs +1 -1
  51. package/src/Rollback.res +62 -41
  52. package/src/Rollback.res.mjs +61 -19
  53. package/src/RollbackCommit.res +7 -9
  54. package/src/RollbackCommit.res.mjs +5 -6
  55. package/src/SimulateItems.res +254 -9
  56. package/src/SimulateItems.res.mjs +214 -47
  57. package/src/Sink.res +35 -26
  58. package/src/Sink.res.mjs +20 -16
  59. package/src/TestIndexer.res +121 -123
  60. package/src/TestIndexer.res.mjs +97 -102
  61. package/src/Utils.res +38 -0
  62. package/src/Utils.res.mjs +39 -2
  63. package/src/Writing.res +4 -2
  64. package/src/Writing.res.mjs +3 -2
  65. package/src/bindings/ClickHouse.res +311 -656
  66. package/src/bindings/ClickHouse.res.mjs +294 -395
  67. package/src/bindings/ClickHouseSink.res +303 -0
  68. package/src/bindings/ClickHouseSink.res.mjs +216 -0
  69. package/src/bindings/NodeJs.res +9 -0
  70. package/src/bindings/NodeJs.res.mjs +8 -1
  71. package/src/bindings/Postgres.res +9 -0
  72. package/src/bindings/Postgres.res.mjs +3 -0
  73. package/src/db/EntityHistory.res +29 -24
  74. package/src/db/EntityHistory.res.mjs +11 -19
  75. package/src/db/InternalTable.res +258 -83
  76. package/src/db/InternalTable.res.mjs +223 -85
  77. package/src/db/RollbackScope.res +29 -0
  78. package/src/db/RollbackScope.res.mjs +31 -0
  79. package/src/db/Table.res +28 -1
  80. package/src/db/Table.res.mjs +26 -2
  81. package/src/resolvers/ResolverProcess.res +809 -0
  82. package/src/resolvers/ResolverProcess.res.mjs +667 -0
  83. package/src/resolvers/ResolverQuery.res +317 -0
  84. package/src/resolvers/ResolverQuery.res.mjs +220 -0
  85. package/src/resolvers/collisions.js +108 -0
  86. package/src/resolvers/db.js +363 -0
  87. package/src/resolvers/dispatch.js +165 -0
  88. package/src/resolvers/errors.js +30 -0
  89. package/src/resolvers/graphqlSelection.js +292 -0
  90. package/src/resolvers/hasuraAction.js +84 -0
  91. package/src/resolvers/hasuraApply.js +323 -0
  92. package/src/resolvers/hasuraMetadata.js +119 -0
  93. package/src/resolvers/index.js +95 -0
  94. package/src/resolvers/manifest.js +370 -0
  95. package/src/resolvers/server.js +270 -0
  96. package/src/sources/AddressSet.res +0 -22
  97. package/src/sources/AddressStore.res +48 -88
  98. package/src/sources/AddressStore.res.mjs +11 -22
  99. package/src/sources/Evm.res +2 -0
  100. package/src/sources/Evm.res.mjs +2 -0
  101. package/src/sources/EvmHyperSyncSource.res +3 -2
  102. package/src/sources/Fuel.res +2 -0
  103. package/src/sources/Fuel.res.mjs +2 -0
  104. package/src/sources/SimulateSource.res +8 -4
  105. package/src/sources/SimulateSource.res.mjs +7 -3
  106. package/src/sources/Svm.res +36 -7
  107. package/src/sources/Svm.res.mjs +34 -4
  108. package/src/sources/SvmHyperSyncClient.res +20 -116
  109. package/src/sources/SvmHyperSyncClient.res.mjs +5 -7
  110. package/src/sources/SvmHyperSyncSource.res +89 -37
  111. package/src/sources/SvmHyperSyncSource.res.mjs +71 -39
  112. package/src/sources/TransactionStore.res +38 -0
  113. package/src/sources/TransactionStore.res.mjs +5 -0
  114. package/src/tui/Tui.res +1 -1
  115. package/src/tui/Tui.res.mjs +1 -1
  116. package/svm.schema.json +44 -156
  117. package/src/MemoryStorage.res +0 -724
  118. package/src/MemoryStorage.res.mjs +0 -601
package/src/Config.res CHANGED
@@ -33,13 +33,19 @@ type sourceConfig =
33
33
  // A `simulate` run: the items the test fed in, parsed against the chain's
34
34
  // registrations. The source itself is built with the chain's address store,
35
35
  // like every other source, so it can apply the same gates.
36
- | SimulateSourceConfig({items: array<Internal.item>, endBlock: int})
36
+ | SimulateSourceConfig({
37
+ items: array<Internal.item>,
38
+ endBlock: int,
39
+ transactionStore?: TransactionStore.t,
40
+ blockStore?: BlockStore.t,
41
+ })
37
42
  // For tests: pass custom sources directly
38
43
  | CustomSources(array<Source.t>)
39
44
 
40
45
  type chain = {
41
46
  name: string,
42
47
  id: ChainId.t,
48
+ ecosystem: Ecosystem.name,
43
49
  startBlock: int,
44
50
  endBlock?: int,
45
51
  maxReorgDepth: int,
@@ -88,6 +94,9 @@ type t = {
88
94
  name: string,
89
95
  description: option<string>,
90
96
  handlers: string,
97
+ // Relative path to the module declaring custom GraphQL resolvers, when the
98
+ // project ships them. None means entity fields only.
99
+ resolvers: option<string>,
91
100
  contractHandlers: array<contractHandler>,
92
101
  shouldRollbackOnReorg: bool,
93
102
  shouldSaveFullHistory: bool,
@@ -101,6 +110,9 @@ type t = {
101
110
  // they can express fits an INTEGER.
102
111
  chainIdMode: ChainId.mode,
103
112
  chainMap: ChainMap.t<chain>,
113
+ // Derived from every chain's contracts, so an id means the same contract on
114
+ // every chain and on every restart.
115
+ contractMapping: ContractMapping.t,
104
116
  defaultChain: option<chain>,
105
117
  ecosystem: Ecosystem.t,
106
118
  enableRawEvents: bool,
@@ -117,75 +129,9 @@ type t = {
117
129
  isDev: bool,
118
130
  userEntitiesByName: dict<Internal.entityConfig>,
119
131
  userEntities: array<Internal.entityConfig>,
120
- allEntities: array<Internal.entityConfig>,
121
132
  allEnums: array<Table.enumConfig<Table.enum>>,
122
133
  }
123
134
 
124
- module EnvioAddresses = {
125
- let name = "envio_addresses"
126
- let index = -1
127
-
128
- let makeId = (~chainId: ChainId.t, ~address) => {
129
- chainId->ChainId.toString ++ "-" ++ address->Address.toString
130
- }
131
-
132
- type t = {
133
- id: string,
134
- @as("chain_id") chainId: ChainId.t,
135
- @as("registration_block") registrationBlock: int,
136
- // Vestigial: always written as -1 and never read back. The column stays so
137
- // an existing schema doesn't need a migration.
138
- @as("registration_log_index") registrationLogIndex: int,
139
- @as("contract_name") contractName: string,
140
- }
141
-
142
- // Extract the raw contract address from the composite id ({chainId}-{address}).
143
- // Inverse of makeId. Keep in sync with makeId above and the SUBSTRING SQL in
144
- // InternalTable.Chains.makeGetInitialStateQuery.
145
- let getAddress = (entity: t): Address.t => {
146
- let sepIdx = entity.id->String.indexOf("-")
147
- entity.id
148
- ->String.slice(~start=sepIdx + 1, ~end=entity.id->String.length)
149
- ->Address.unsafeFromString
150
- }
151
-
152
- let schema = S.schema(s => {
153
- id: s.matches(S.string),
154
- chainId: s.matches(ChainId.schema),
155
- registrationBlock: s.matches(S.int),
156
- registrationLogIndex: s.matches(S.int),
157
- contractName: s.matches(S.string),
158
- })
159
-
160
- let table = Table.mkTable(
161
- name,
162
- ~fields=[
163
- Table.mkField("id", String, ~isPrimaryKey=true, ~fieldSchema=S.string),
164
- Table.mkField("chain_id", ChainId, ~fieldSchema=ChainId.schema),
165
- Table.mkField("registration_block", Int32, ~fieldSchema=S.int),
166
- // -1 sentinel when registered from a block handler (no log index)
167
- Table.mkField("registration_log_index", Int32, ~fieldSchema=S.int),
168
- Table.mkField("contract_name", String, ~fieldSchema=S.string),
169
- ],
170
- )
171
-
172
- external castToInternal: t => Internal.entity = "%identity"
173
-
174
- let entityConfig = {
175
- Internal.name,
176
- index,
177
- schema,
178
- table,
179
- // Internal address tracking is Postgres-only; the global config is
180
- // always required to have Postgres enabled (Storage::resolve forbids
181
- // a Postgres-disabled global), so this is safe regardless of mode.
182
- storage: {postgres: true, clickhouse: false},
183
- // The table already keys rows by chain through the composite `id`, so the
184
- // per-chain mode must not append a second chain-id column to it.
185
- crossChain: true,
186
- }->Internal.fromGenericEntityConfig
187
- }
188
-
189
135
  type rpcSourceFor = | @as("sync") Sync | @as("fallback") Fallback | @as("realtime") Realtime
190
136
 
191
137
  let rpcSourceForSchema = S.enum([Sync, Fallback, Realtime])
@@ -234,27 +180,6 @@ let publicConfigChainSchema = S.schema(s =>
234
180
  let svmEventDescriptorSchema = S.schema(s =>
235
181
  {
236
182
  "discriminator": s.matches(S.option(S.string)),
237
- "discriminatorByteLen": s.matches(S.int),
238
- "transactionFields": s.matches(S.array(Internal.svmTransactionFieldSchema)),
239
- "blockFields": s.matches(S.option(S.array(Internal.svmBlockFieldSchema))),
240
- "includeLogs": s.matches(S.bool),
241
- // An array of AND-groups OR-ed together: the CLI normalizes both the flat
242
- // and `any_of` YAML shapes to `Vec<Vec<SvmAccountFilterJson>>`.
243
- "accountFilters": s.matches(
244
- S.option(
245
- S.array(
246
- S.array(
247
- S.schema(s =>
248
- {
249
- "position": s.matches(S.int),
250
- "values": s.matches(S.array(S.string)),
251
- }
252
- ),
253
- ),
254
- ),
255
- ),
256
- ),
257
- "isInner": s.matches(S.option(S.bool)),
258
183
  "accounts": s.matches(S.option(S.array(S.string))),
259
184
  "args": s.matches(S.option(S.json(~validate=false))),
260
185
  }
@@ -348,10 +273,18 @@ let propertySchema = S.schema(s =>
348
273
  }
349
274
  )
350
275
 
276
+ let clickhouseSkippingIndexSchema: S.t<Internal.clickhouseSkippingIndex> = S.object(s => {
277
+ Internal.name: s.field("name", S.string),
278
+ expr: s.field("expr", S.string),
279
+ type_: s.field("type", S.string),
280
+ granularity: ?s.field("granularity", S.option(S.int)),
281
+ })
282
+
351
283
  let clickhouseTableOptionsSchema: S.t<Internal.clickhouseTableOptions> = S.object(s => {
352
284
  Internal.partitionBy: ?s.field("partitionBy", S.option(S.string)),
353
285
  orderBy: ?s.field("orderBy", S.option(S.array(S.string))),
354
286
  ttl: ?s.field("ttl", S.option(S.string)),
287
+ skippingIndexes: ?s.field("skippingIndexes", S.option(S.array(clickhouseSkippingIndexSchema))),
355
288
  })
356
289
 
357
290
  // The entity's `clickhouse` storage arg mirrors the @storage directive:
@@ -377,6 +310,7 @@ let entityJsonSchema = S.schema(s =>
377
310
  "name": s.matches(S.string),
378
311
  "crossChain": s.matches(S.option(S.bool)),
379
312
  "storage": s.matches(S.option(entityStorageSchema)),
313
+ "internal": s.matches(S.option(S.bool)),
380
314
  "properties": s.matches(S.array(propertySchema)),
381
315
  "derivedFields": s.matches(S.option(S.array(derivedFieldSchema))),
382
316
  "compositeIndices": s.matches(S.option(S.array(S.array(compositeIndexFieldSchema)))),
@@ -431,7 +365,7 @@ let getFieldTypeAndSchema = (prop, ~enumConfigsByName: dict<Table.enumConfig<Tab
431
365
  baseSchema
432
366
  }
433
367
  let fieldSchema = if isNullable {
434
- S.null(fieldSchema)->S.toUnknown
368
+ Utils.Schema.nullTolerant(fieldSchema)->S.toUnknown
435
369
  } else {
436
370
  fieldSchema
437
371
  }
@@ -580,6 +514,7 @@ let parseEntitiesFromJson = (
580
514
  table,
581
515
  storage,
582
516
  crossChain,
517
+ internal: entityJson["internal"]->Option.getOr(false),
583
518
  }->Internal.fromGenericEntityConfig
584
519
  })
585
520
  }
@@ -600,6 +535,7 @@ let publicConfigSchema = S.schema(s =>
600
535
  "name": s.matches(S.string),
601
536
  "description": s.matches(S.option(S.string)),
602
537
  "handlers": s.matches(S.option(S.string)),
538
+ "resolvers": s.matches(S.option(S.string)),
603
539
  "isDev": s.matches(S.option(S.bool)),
604
540
  "fullBatchSize": s.matches(S.option(S.int)),
605
541
  "rollbackOnReorg": s.matches(S.option(S.bool)),
@@ -616,6 +552,14 @@ let publicConfigSchema = S.schema(s =>
616
552
  }
617
553
  )
618
554
 
555
+ let contractMappingOf = (~chainConfigs: array<chain>): ContractMapping.t => {
556
+ let names = []
557
+ chainConfigs->Array.forEach(chain =>
558
+ chain.contracts->Array.forEach(contract => names->Array.push(contract.name)->ignore)
559
+ )
560
+ ContractMapping.make(~names)
561
+ }
562
+
619
563
  let fromPublic = (publicConfigJson: JSON.t) => {
620
564
  let maxAddrInPartition = Env.maxAddrInPartition
621
565
  // Parse public config
@@ -795,14 +739,6 @@ let fromPublic = (publicConfigJson: JSON.t) => {
795
739
  Utils.magic: _ => {
796
740
  "svm": option<{
797
741
  "discriminator": option<string>,
798
- "discriminatorByteLen": int,
799
- "transactionFields": array<Internal.svmTransactionField>,
800
- "blockFields": option<array<Internal.svmBlockField>>,
801
- "includeLogs": bool,
802
- "accountFilters": option<
803
- array<array<{"position": int, "values": array<string>}>>,
804
- >,
805
- "isInner": option<bool>,
806
742
  "accounts": option<array<string>>,
807
743
  "args": option<JSON.t>,
808
744
  }>,
@@ -815,28 +751,11 @@ let fromPublic = (publicConfigJson: JSON.t) => {
815
751
  `SVM instruction ${contractName}.${eventName} is missing the "svm" descriptor in internal config`,
816
752
  )
817
753
  }
818
- let accountFilters =
819
- svm["accountFilters"]
820
- ->Option.getOr([])
821
- ->Array.map(group =>
822
- group->Array.map(
823
- af => {
824
- Internal.position: af["position"],
825
- values: af["values"]->SvmTypes.Pubkey.fromStringsUnsafe,
826
- },
827
- )
828
- )
829
754
  (EventConfigBuilder.buildSvmInstructionEventConfig(
830
755
  ~contractName,
831
756
  ~instructionName=eventName,
832
757
  ~programId,
833
758
  ~discriminator=svm["discriminator"],
834
- ~discriminatorByteLen=svm["discriminatorByteLen"],
835
- ~transactionFields=svm["transactionFields"],
836
- ~blockFields=?svm["blockFields"],
837
- ~includeLogs=svm["includeLogs"],
838
- ~accountFilters,
839
- ~isInner=svm["isInner"],
840
759
  ~accounts=svm["accounts"]->Option.getOr([]),
841
760
  ~args=svm["args"]->Option.getOr(JSON.Null),
842
761
  ~definedTypes=svmDefinedTypes,
@@ -923,25 +842,24 @@ let fromPublic = (publicConfigJson: JSON.t) => {
923
842
  }
924
843
  })
925
844
 
926
- // The same address under two contract definitions (or twice under one)
927
- // would later violate the (chainId, address) primary key of
928
- // envio_addresses with an opaque Postgres error — fail fast with the
929
- // offending pair instead. parseAddress already canonicalizes casing
930
- // (checksum or lowercase), so an exact match catches case variants too.
931
- let contractNameByAddress = Dict.make()
845
+ // One address listed twice for the same contract would violate the
846
+ // (chainId, address, contract) primary key of envio_addresses with an
847
+ // opaque Postgres error — fail fast instead. Two contracts may share an
848
+ // address: each indexes it with its own events. parseAddress already
849
+ // canonicalizes casing (checksum or lowercase), so an exact match
850
+ // catches case variants too.
851
+ let seen = Utils.Set.make()
932
852
  contracts->Array.forEach(contract => {
933
853
  contract.addresses->Array.forEach(
934
854
  address => {
935
855
  let addressString = address->Address.toString
936
- switch contractNameByAddress->Dict.get(addressString) {
937
- | Some(existingContractName) =>
856
+ let key = contract.name ++ "-" ++ addressString
857
+ if seen->Utils.Set.has(key) {
938
858
  JsError.throwWithMessage(
939
- existingContractName === contract.name
940
- ? `Address ${addressString} is listed multiple times for the contract ${contract.name} on chain ${chainId->ChainId.toString}. Please remove the duplicate from your config.`
941
- : `Address ${addressString} on chain ${chainId->ChainId.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.`,
859
+ `Address ${addressString} is listed multiple times for the contract ${contract.name} on chain ${chainId->ChainId.toString}. Please remove the duplicate from your config.`,
942
860
  )
943
- | None => contractNameByAddress->Dict.set(addressString, contract.name)
944
861
  }
862
+ seen->Utils.Set.add(key)->ignore
945
863
  },
946
864
  )
947
865
  })
@@ -1012,6 +930,7 @@ let fromPublic = (publicConfigJson: JSON.t) => {
1012
930
  {
1013
931
  name: chainName,
1014
932
  id: chainId,
933
+ ecosystem: ecosystemName,
1015
934
  startBlock: publicChainConfig["startBlock"],
1016
935
  endBlock: ?publicChainConfig["endBlock"],
1017
936
  maxReorgDepth: switch ecosystemName {
@@ -1066,8 +985,6 @@ let fromPublic = (publicConfigJson: JSON.t) => {
1066
985
  ->Option.getOr([])
1067
986
  ->parseEntitiesFromJson(~enumConfigsByName, ~globalStorage, ~defaultCrossChain)
1068
987
 
1069
- let allEntities = userEntities->Array.concat([EnvioAddresses.entityConfig])
1070
-
1071
988
  // Keyed by the capitalized entity name to match the handler-context
1072
989
  // accessor (`context.Pool_snapshots`) the generated types expose, while
1073
990
  // entityConfig.name stays the original schema name used for the physical
@@ -1097,6 +1014,7 @@ let fromPublic = (publicConfigJson: JSON.t) => {
1097
1014
  name: publicConfig["name"],
1098
1015
  description: publicConfig["description"],
1099
1016
  handlers: publicConfig["handlers"]->Option.getOr("src/handlers"),
1017
+ resolvers: publicConfig["resolvers"],
1100
1018
  contractHandlers,
1101
1019
  shouldRollbackOnReorg: publicConfig["rollbackOnReorg"]->Option.getOr(true),
1102
1020
  shouldSaveFullHistory: publicConfig["saveFullHistory"]->Option.getOr(false),
@@ -1104,6 +1022,7 @@ let fromPublic = (publicConfigJson: JSON.t) => {
1104
1022
  storage: globalStorage,
1105
1023
  chainIdMode: publicConfig["chainIdMode"]->Option.getOr(Int32),
1106
1024
  chainMap,
1025
+ contractMapping: contractMappingOf(~chainConfigs=chains),
1107
1026
  defaultChain: chains->Array.get(0),
1108
1027
  enableRawEvents: publicConfig["rawEvents"]->Option.getOr(false),
1109
1028
  ecosystem,
@@ -1115,11 +1034,18 @@ let fromPublic = (publicConfigJson: JSON.t) => {
1115
1034
  isDev: publicConfig["isDev"]->Option.getOr(false),
1116
1035
  userEntitiesByName,
1117
1036
  userEntities,
1118
- allEntities,
1119
1037
  allEnums,
1120
1038
  }
1121
1039
  }
1122
1040
 
1041
+ // With no cross-chain entity, a reorg on one chain can never have changed a row
1042
+ // another chain owns, so its rollback stays isolated to that chain instead of
1043
+ // dragging every sibling back with it. A single chain has no sibling to spare,
1044
+ // and narrowing its rollback would only buy it a predicate that always holds.
1045
+ let isIsolatedMultichain = (config: t) =>
1046
+ config.chainMap->ChainMap.keys->Array.length > 1 &&
1047
+ config.userEntities->Array.every(entityConfig => !entityConfig.crossChain)
1048
+
1123
1049
  // Canonicalize a user-provided address to the configured casing so it matches
1124
1050
  // addresses parsed from config.yaml during routing. HyperSync/RPC data arrives
1125
1051
  // already canonical; only user-land input (simulate srcAddress, contractRegister
@@ -1222,8 +1148,11 @@ let getPublicConfigJson = () =>
1222
1148
 
1223
1149
  // Drops source URLs from each chain so RPC/hypersync edits don't trigger
1224
1150
  // the resume-time compat check (and don't end up in `envio_info`). Also
1225
- // drops `isDev`, which toggles between `envio dev` and `envio start` and
1226
- // has no bearing on schema/indexing compatibility.
1151
+ // drops two fields that have no bearing on schema/indexing compatibility:
1152
+ // `isDev`, which toggles between `envio dev` and `envio start`, and
1153
+ // `resolvers`, which names where the custom GraphQL resolvers live. Without
1154
+ // the latter, adding `resolvers:` to a running indexer reads as an
1155
+ // incompatible config change and it refuses to resume.
1227
1156
  let stripSensitiveData = (json: JSON.t): JSON.t => {
1228
1157
  let cloned = json->JSON.stringify->JSON.parseOrThrow
1229
1158
  let stripChains = (ecosystem: option<JSON.t>) =>
@@ -1250,6 +1179,7 @@ let stripSensitiveData = (json: JSON.t): JSON.t => {
1250
1179
  switch cloned {
1251
1180
  | Object(obj) => {
1252
1181
  obj->Utils.Dict.deleteInPlace("isDev")
1182
+ obj->Utils.Dict.deleteInPlace("resolvers")
1253
1183
  stripChains(obj->Dict.get("evm"))
1254
1184
  stripChains(obj->Dict.get("fuel"))
1255
1185
  stripChains(obj->Dict.get("svm"))
@@ -16,6 +16,7 @@ import * as BigDecimal from "./bindings/BigDecimal.res.mjs";
16
16
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
17
17
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
18
18
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
19
+ import * as ContractMapping from "./ContractMapping.res.mjs";
19
20
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
20
21
  import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
21
22
  import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
@@ -32,57 +33,6 @@ function chainIdColumnName(format) {
32
33
  }
33
34
  }
34
35
 
35
- let name = "envio_addresses";
36
-
37
- function makeId(chainId, address) {
38
- return ChainId.toString(chainId) + "-" + address;
39
- }
40
-
41
- function getAddress(entity) {
42
- let sepIdx = entity.id.indexOf("-");
43
- return entity.id.slice(sepIdx + 1 | 0, entity.id.length);
44
- }
45
-
46
- let schema = S$RescriptSchema.schema(s => ({
47
- id: s.m(S$RescriptSchema.string),
48
- chain_id: s.m(ChainId.schema),
49
- registration_block: s.m(S$RescriptSchema.int),
50
- registration_log_index: s.m(S$RescriptSchema.int),
51
- contract_name: s.m(S$RescriptSchema.string)
52
- }));
53
-
54
- let table = Table.mkTable(name, undefined, [
55
- Table.mkField("id", "String", S$RescriptSchema.string, undefined, undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined),
56
- Table.mkField("chain_id", "ChainId", ChainId.schema, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
57
- Table.mkField("registration_block", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
58
- Table.mkField("registration_log_index", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
59
- Table.mkField("contract_name", "String", S$RescriptSchema.string, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined)
60
- ], undefined);
61
-
62
- let entityConfig_storage = {
63
- postgres: true,
64
- clickhouse: false
65
- };
66
-
67
- let entityConfig = {
68
- name: name,
69
- index: -1,
70
- schema: schema,
71
- table: table,
72
- storage: entityConfig_storage,
73
- crossChain: true
74
- };
75
-
76
- let EnvioAddresses = {
77
- name: name,
78
- index: -1,
79
- makeId: makeId,
80
- getAddress: getAddress,
81
- schema: schema,
82
- table: table,
83
- entityConfig: entityConfig
84
- };
85
-
86
36
  let rpcSourceForSchema = S$RescriptSchema.$$enum([
87
37
  "sync",
88
38
  "fallback",
@@ -123,15 +73,6 @@ let publicConfigChainSchema = S$RescriptSchema.schema(s => ({
123
73
 
124
74
  let svmEventDescriptorSchema = S$RescriptSchema.schema(s => ({
125
75
  discriminator: s.m(S$RescriptSchema.option(S$RescriptSchema.string)),
126
- discriminatorByteLen: s.m(S$RescriptSchema.int),
127
- transactionFields: s.m(S$RescriptSchema.array(Internal.svmTransactionFieldSchema)),
128
- blockFields: s.m(S$RescriptSchema.option(S$RescriptSchema.array(Internal.svmBlockFieldSchema))),
129
- includeLogs: s.m(S$RescriptSchema.bool),
130
- accountFilters: s.m(S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.array(S$RescriptSchema.schema(s => ({
131
- position: s.m(S$RescriptSchema.int),
132
- values: s.m(S$RescriptSchema.array(S$RescriptSchema.string))
133
- })))))),
134
- isInner: s.m(S$RescriptSchema.option(S$RescriptSchema.bool)),
135
76
  accounts: s.m(S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.string))),
136
77
  args: s.m(S$RescriptSchema.option(S$RescriptSchema.json(false)))
137
78
  }));
@@ -204,10 +145,18 @@ let propertySchema = S$RescriptSchema.schema(s => ({
204
145
  description: s.m(S$RescriptSchema.option(S$RescriptSchema.string))
205
146
  }));
206
147
 
148
+ let clickhouseSkippingIndexSchema = S$RescriptSchema.object(s => ({
149
+ name: s.f("name", S$RescriptSchema.string),
150
+ expr: s.f("expr", S$RescriptSchema.string),
151
+ type: s.f("type", S$RescriptSchema.string),
152
+ granularity: s.f("granularity", S$RescriptSchema.option(S$RescriptSchema.int))
153
+ }));
154
+
207
155
  let clickhouseTableOptionsSchema = S$RescriptSchema.object(s => ({
208
156
  partitionBy: s.f("partitionBy", S$RescriptSchema.option(S$RescriptSchema.string)),
209
157
  orderBy: s.f("orderBy", S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.string))),
210
- ttl: s.f("ttl", S$RescriptSchema.option(S$RescriptSchema.string))
158
+ ttl: s.f("ttl", S$RescriptSchema.option(S$RescriptSchema.string)),
159
+ skippingIndexes: s.f("skippingIndexes", S$RescriptSchema.option(S$RescriptSchema.array(clickhouseSkippingIndexSchema)))
211
160
  }));
212
161
 
213
162
  let entityClickhouseStorageJsonSchema = S$RescriptSchema.union([
@@ -230,6 +179,7 @@ let entityJsonSchema = S$RescriptSchema.schema(s => ({
230
179
  name: s.m(S$RescriptSchema.string),
231
180
  crossChain: s.m(S$RescriptSchema.option(S$RescriptSchema.bool)),
232
181
  storage: s.m(S$RescriptSchema.option(entityStorageSchema)),
182
+ internal: s.m(S$RescriptSchema.option(S$RescriptSchema.bool)),
233
183
  properties: s.m(S$RescriptSchema.array(propertySchema)),
234
184
  derivedFields: s.m(S$RescriptSchema.option(S$RescriptSchema.array(derivedFieldSchema))),
235
185
  compositeIndices: s.m(S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.array(compositeIndexFieldSchema)))),
@@ -322,7 +272,7 @@ function getFieldTypeAndSchema(prop, enumConfigsByName) {
322
272
  }
323
273
  let baseSchema = match[1];
324
274
  let fieldSchema = isArray ? S$RescriptSchema.array(baseSchema) : baseSchema;
325
- let fieldSchema$1 = isNullable ? S$RescriptSchema.$$null(fieldSchema) : fieldSchema;
275
+ let fieldSchema$1 = isNullable ? Utils.Schema.nullTolerant(fieldSchema) : fieldSchema;
326
276
  return [
327
277
  match[0],
328
278
  fieldSchema$1,
@@ -400,7 +350,8 @@ function parseEntitiesFromJson(entitiesJson, enumConfigsByName, globalStorage, d
400
350
  schema: schema,
401
351
  table: table,
402
352
  storage: storage,
403
- crossChain: crossChain
353
+ crossChain: crossChain,
354
+ internal: Stdlib_Option.getOr(entityJson.internal, false)
404
355
  };
405
356
  });
406
357
  }
@@ -421,6 +372,7 @@ let publicConfigSchema = S$RescriptSchema.schema(s => ({
421
372
  name: s.m(S$RescriptSchema.string),
422
373
  description: s.m(S$RescriptSchema.option(S$RescriptSchema.string)),
423
374
  handlers: s.m(S$RescriptSchema.option(S$RescriptSchema.string)),
375
+ resolvers: s.m(S$RescriptSchema.option(S$RescriptSchema.string)),
424
376
  isDev: s.m(S$RescriptSchema.option(S$RescriptSchema.bool)),
425
377
  fullBatchSize: s.m(S$RescriptSchema.option(S$RescriptSchema.int)),
426
378
  rollbackOnReorg: s.m(S$RescriptSchema.option(S$RescriptSchema.bool)),
@@ -436,6 +388,16 @@ let publicConfigSchema = S$RescriptSchema.schema(s => ({
436
388
  entities: s.m(S$RescriptSchema.option(S$RescriptSchema.array(entityJsonSchema)))
437
389
  }));
438
390
 
391
+ function contractMappingOf(chainConfigs) {
392
+ let names = [];
393
+ chainConfigs.forEach(chain => {
394
+ chain.contracts.forEach(contract => {
395
+ names.push(contract.name);
396
+ });
397
+ });
398
+ return ContractMapping.make(names);
399
+ }
400
+
439
401
  function fromPublic(publicConfigJson) {
440
402
  let publicConfig;
441
403
  try {
@@ -560,11 +522,7 @@ function fromPublic(publicConfigJson) {
560
522
  ) : addresses[0];
561
523
  let s = eventItem.svm;
562
524
  let svm = s !== undefined ? Primitive_option.valFromOption(s) : Stdlib_JsError.throwWithMessage(`SVM instruction ` + contractName + `.` + eventName + ` is missing the "svm" descriptor in internal config`);
563
- let accountFilters = Stdlib_Option.getOr(svm.accountFilters, []).map(group => group.map(af => ({
564
- position: af.position,
565
- values: af.values
566
- })));
567
- return EventConfigBuilder.buildSvmInstructionEventConfig(contractName, eventName, programId, svm.discriminator, svm.discriminatorByteLen, svm.includeLogs, svm.transactionFields, svm.blockFields, accountFilters, svm.isInner, Stdlib_Option.getOr(svm.accounts, []), Stdlib_Option.getOr(svm.args, null), svmDefinedTypes);
525
+ return EventConfigBuilder.buildSvmInstructionEventConfig(contractName, eventName, programId, svm.discriminator, Stdlib_Option.getOr(svm.accounts, []), Stdlib_Option.getOr(svm.args, null), svmDefinedTypes);
568
526
  }
569
527
  });
570
528
  } else {
@@ -614,16 +572,14 @@ function fromPublic(publicConfigJson) {
614
572
  startBlock: startBlock
615
573
  };
616
574
  });
617
- let contractNameByAddress = {};
575
+ let seen = new Set();
618
576
  contracts.forEach(contract => {
619
577
  contract.addresses.forEach(address => {
620
- let existingContractName = contractNameByAddress[address];
621
- if (existingContractName !== undefined) {
622
- return Stdlib_JsError.throwWithMessage(existingContractName === contract.name ? `Address ` + address + ` is listed multiple times for the contract ` + contract.name + ` on chain ` + ChainId.toString(chainId) + `. Please remove the duplicate from your config.` : `Address ` + address + ` on chain ` + ChainId.toString(chainId) + ` 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.`);
623
- } else {
624
- contractNameByAddress[address] = contract.name;
625
- return;
578
+ let key = contract.name + "-" + address;
579
+ if (seen.has(key)) {
580
+ Stdlib_JsError.throwWithMessage(`Address ` + address + ` is listed multiple times for the contract ` + contract.name + ` on chain ` + ChainId.toString(chainId) + `. Please remove the duplicate from your config.`);
626
581
  }
582
+ seen.add(key);
627
583
  });
628
584
  });
629
585
  let sourceConfig;
@@ -696,6 +652,7 @@ function fromPublic(publicConfigJson) {
696
652
  return {
697
653
  name: chainName,
698
654
  id: chainId,
655
+ ecosystem: ecosystemName,
699
656
  startBlock: publicChainConfig.startBlock,
700
657
  endBlock: publicChainConfig.endBlock,
701
658
  maxReorgDepth: tmp,
@@ -725,7 +682,6 @@ function fromPublic(publicConfigJson) {
725
682
  };
726
683
  let defaultCrossChain = Stdlib_Option.getOr(publicConfig.defaultCrossChain, true);
727
684
  let userEntities = parseEntitiesFromJson(Stdlib_Option.getOr(publicConfig.entities, []), enumConfigsByName, globalStorage, defaultCrossChain);
728
- let allEntities = userEntities.concat([entityConfig]);
729
685
  let userEntitiesByName = Object.fromEntries(userEntities.map(entityConfig => [
730
686
  Utils.$$String.capitalize(entityConfig.name),
731
687
  entityConfig
@@ -738,6 +694,7 @@ function fromPublic(publicConfigJson) {
738
694
  name: publicConfig.name,
739
695
  description: publicConfig.description,
740
696
  handlers: Stdlib_Option.getOr(publicConfig.handlers, "src/handlers"),
697
+ resolvers: publicConfig.resolvers,
741
698
  contractHandlers: contractHandlers,
742
699
  shouldRollbackOnReorg: Stdlib_Option.getOr(publicConfig.rollbackOnReorg, true),
743
700
  shouldSaveFullHistory: Stdlib_Option.getOr(publicConfig.saveFullHistory, false),
@@ -745,6 +702,7 @@ function fromPublic(publicConfigJson) {
745
702
  storage: globalStorage,
746
703
  chainIdMode: Stdlib_Option.getOr(publicConfig.chainIdMode, "int32"),
747
704
  chainMap: chainMap,
705
+ contractMapping: contractMappingOf(chains),
748
706
  defaultChain: chains[0],
749
707
  ecosystem: ecosystem,
750
708
  enableRawEvents: Stdlib_Option.getOr(publicConfig.rawEvents, false),
@@ -756,11 +714,18 @@ function fromPublic(publicConfigJson) {
756
714
  isDev: Stdlib_Option.getOr(publicConfig.isDev, false),
757
715
  userEntitiesByName: userEntitiesByName,
758
716
  userEntities: userEntities,
759
- allEntities: allEntities,
760
717
  allEnums: allEnums
761
718
  };
762
719
  }
763
720
 
721
+ function isIsolatedMultichain(config) {
722
+ if (ChainMap.keys(config.chainMap).length > 1) {
723
+ return config.userEntities.every(entityConfig => !entityConfig.crossChain);
724
+ } else {
725
+ return false;
726
+ }
727
+ }
728
+
764
729
  function normalizeUserAddress(config, address) {
765
730
  let match = config.ecosystem.name;
766
731
  switch (match) {
@@ -899,6 +864,7 @@ function stripSensitiveData(json) {
899
864
  };
900
865
  if (typeof cloned === "object" && cloned !== null && !Array.isArray(cloned)) {
901
866
  Utils.Dict.deleteInPlace(cloned, "isDev");
867
+ Utils.Dict.deleteInPlace(cloned, "resolvers");
902
868
  stripChains(cloned["evm"]);
903
869
  stripChains(cloned["fuel"]);
904
870
  stripChains(cloned["svm"]);
@@ -1083,7 +1049,6 @@ function getPgUserEntities(config) {
1083
1049
  export {
1084
1050
  chainIdFieldName,
1085
1051
  chainIdColumnName,
1086
- EnvioAddresses,
1087
1052
  rpcSourceForSchema,
1088
1053
  rpcConfigSchema,
1089
1054
  chainContractSchema,
@@ -1097,6 +1062,7 @@ export {
1097
1062
  compositeIndexFieldSchema,
1098
1063
  derivedFieldSchema,
1099
1064
  propertySchema,
1065
+ clickhouseSkippingIndexSchema,
1100
1066
  clickhouseTableOptionsSchema,
1101
1067
  entityClickhouseStorageJsonSchema,
1102
1068
  entityStorageSchema,
@@ -1108,7 +1074,9 @@ export {
1108
1074
  columnNameFormatSchema,
1109
1075
  publicConfigStorageSchema,
1110
1076
  publicConfigSchema,
1077
+ contractMappingOf,
1111
1078
  fromPublic,
1079
+ isIsolatedMultichain,
1112
1080
  normalizeUserAddress,
1113
1081
  normalizeSimulateAddress,
1114
1082
  getEventConfig,
@@ -1124,4 +1092,4 @@ export {
1124
1092
  load,
1125
1093
  getPgUserEntities,
1126
1094
  }
1127
- /* schema Not a pure module */
1095
+ /* rpcSourceForSchema Not a pure module */