envio 3.1.2 → 3.2.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.
- package/evm.schema.json +83 -11
- package/fuel.schema.json +83 -11
- package/index.d.ts +184 -3
- package/package.json +6 -6
- package/src/Batch.res +2 -2
- package/src/ChainFetcher.res +27 -3
- package/src/ChainFetcher.res.mjs +17 -3
- package/src/ChainManager.res +163 -0
- package/src/ChainManager.res.mjs +136 -0
- package/src/Config.res +213 -30
- package/src/Config.res.mjs +102 -41
- package/src/Core.res +16 -10
- package/src/Ecosystem.res +0 -3
- package/src/Env.res +2 -2
- package/src/Env.res.mjs +2 -2
- package/src/Envio.res +101 -2
- package/src/Envio.res.mjs +2 -3
- package/src/EventConfigBuilder.res +52 -0
- package/src/EventConfigBuilder.res.mjs +32 -0
- package/src/EventUtils.res +2 -2
- package/src/FetchState.res +23 -14
- package/src/FetchState.res.mjs +21 -15
- package/src/GlobalState.res +219 -363
- package/src/GlobalState.res.mjs +314 -491
- package/src/GlobalStateManager.res +49 -59
- package/src/GlobalStateManager.res.mjs +5 -4
- package/src/GlobalStateManager.resi +1 -1
- package/src/HandlerLoader.res +12 -1
- package/src/HandlerLoader.res.mjs +6 -1
- package/src/HandlerRegister.res +9 -9
- package/src/HandlerRegister.res.mjs +9 -9
- package/src/Hasura.res +102 -32
- package/src/Hasura.res.mjs +88 -34
- package/src/InMemoryStore.res +10 -1
- package/src/InMemoryStore.res.mjs +4 -1
- package/src/InMemoryTable.res +83 -136
- package/src/InMemoryTable.res.mjs +57 -86
- package/src/Internal.res +54 -5
- package/src/Internal.res.mjs +2 -8
- package/src/LazyLoader.res +2 -2
- package/src/LazyLoader.res.mjs +3 -3
- package/src/LoadLayer.res +47 -60
- package/src/LoadLayer.res.mjs +28 -50
- package/src/LoadLayer.resi +2 -5
- package/src/LogSelection.res +4 -4
- package/src/LogSelection.res.mjs +5 -7
- package/src/Logging.res +1 -1
- package/src/Main.res +61 -2
- package/src/Main.res.mjs +37 -1
- package/src/Persistence.res +3 -16
- package/src/PgStorage.res +125 -114
- package/src/PgStorage.res.mjs +112 -95
- package/src/Ports.res +5 -0
- package/src/Ports.res.mjs +9 -0
- package/src/Prometheus.res +3 -3
- package/src/Prometheus.res.mjs +4 -4
- package/src/ReorgDetection.res +4 -4
- package/src/ReorgDetection.res.mjs +4 -5
- package/src/SafeCheckpointTracking.res +16 -16
- package/src/SafeCheckpointTracking.res.mjs +2 -2
- package/src/SimulateItems.res +10 -14
- package/src/SimulateItems.res.mjs +5 -2
- package/src/Sink.res +1 -1
- package/src/Sink.res.mjs +1 -2
- package/src/SvmTypes.res +9 -0
- package/src/SvmTypes.res.mjs +14 -0
- package/src/TestIndexer.res +17 -57
- package/src/TestIndexer.res.mjs +14 -48
- package/src/TestIndexerProxyStorage.res +23 -23
- package/src/TestIndexerProxyStorage.res.mjs +12 -15
- package/src/Throttler.res +2 -2
- package/src/Time.res +2 -2
- package/src/Time.res.mjs +2 -2
- package/src/UserContext.res +19 -118
- package/src/UserContext.res.mjs +10 -66
- package/src/Utils.res +15 -15
- package/src/Utils.res.mjs +7 -8
- package/src/adapters/MarkBatchProcessedAdapter.res +5 -0
- package/src/adapters/MarkBatchProcessedAdapter.res.mjs +14 -0
- package/src/bindings/BigDecimal.res +1 -1
- package/src/bindings/BigDecimal.res.mjs +2 -2
- package/src/bindings/ClickHouse.res +8 -6
- package/src/bindings/ClickHouse.res.mjs +5 -5
- package/src/bindings/Hrtime.res +1 -1
- package/src/bindings/Pino.res +2 -2
- package/src/bindings/Pino.res.mjs +3 -4
- package/src/db/EntityFilter.res +410 -0
- package/src/db/EntityFilter.res.mjs +424 -0
- package/src/db/EntityHistory.res +1 -1
- package/src/db/EntityHistory.res.mjs +1 -1
- package/src/db/InternalTable.res +10 -10
- package/src/db/InternalTable.res.mjs +41 -45
- package/src/db/Schema.res +2 -2
- package/src/db/Schema.res.mjs +3 -3
- package/src/db/Table.res +106 -22
- package/src/db/Table.res.mjs +84 -35
- package/src/sources/EventRouter.res +67 -2
- package/src/sources/EventRouter.res.mjs +45 -3
- package/src/sources/Evm.res +0 -7
- package/src/sources/Evm.res.mjs +0 -15
- package/src/sources/EvmChain.res +1 -1
- package/src/sources/EvmChain.res.mjs +1 -2
- package/src/sources/EvmRpcClient.res +42 -0
- package/src/sources/EvmRpcClient.res.mjs +64 -0
- package/src/sources/Fuel.res +0 -7
- package/src/sources/Fuel.res.mjs +0 -15
- package/src/sources/HyperFuelSource.res +5 -4
- package/src/sources/HyperFuelSource.res.mjs +2 -2
- package/src/sources/HyperSyncClient.res +9 -5
- package/src/sources/HyperSyncClient.res.mjs +2 -2
- package/src/sources/HyperSyncHeightStream.res +2 -2
- package/src/sources/HyperSyncHeightStream.res.mjs +2 -2
- package/src/sources/HyperSyncSource.res +10 -9
- package/src/sources/HyperSyncSource.res.mjs +4 -4
- package/src/sources/Rpc.res +1 -5
- package/src/sources/Rpc.res.mjs +1 -9
- package/src/sources/RpcSource.res +57 -21
- package/src/sources/RpcSource.res.mjs +47 -20
- package/src/sources/RpcWebSocketHeightStream.res +1 -1
- package/src/sources/SourceManager.res +3 -2
- package/src/sources/SourceManager.res.mjs +1 -1
- package/src/sources/Svm.res +3 -10
- package/src/sources/Svm.res.mjs +4 -18
- package/src/sources/SvmHyperSyncClient.res +265 -0
- package/src/sources/SvmHyperSyncClient.res.mjs +28 -0
- package/src/sources/SvmHyperSyncSource.res +638 -0
- package/src/sources/SvmHyperSyncSource.res.mjs +557 -0
- package/src/tui/Tui.res +9 -2
- package/src/tui/Tui.res.mjs +18 -3
- package/src/tui/components/BufferedProgressBar.res +2 -2
- package/src/tui/components/TuiData.res +3 -0
- package/svm.schema.json +523 -14
- package/src/TableIndices.res +0 -115
- 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->
|
|
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
|
|
412
|
-
// to match the
|
|
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
|
-
|
|
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
|
-
|
|
523
|
-
|
|
524
|
-
|
|
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<{
|
|
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
|
-
{
|
|
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
|
|
806
|
+
let rawAddresses =
|
|
655
807
|
chainContract
|
|
656
808
|
->Option.flatMap(cc => cc["addresses"])
|
|
657
809
|
->Option.getOr([])
|
|
658
|
-
|
|
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
|
-
|
|
736
|
-
|
|
737
|
-
|
|
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
|
-
->
|
|
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->
|
|
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->
|
|
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
|