envio 3.0.2-svm-alpha.0 → 3.0.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/evm.schema.json +8 -8
- package/fuel.schema.json +12 -12
- package/index.d.ts +1 -155
- package/package.json +7 -6
- package/src/ChainFetcher.res +1 -25
- package/src/ChainFetcher.res.mjs +1 -19
- package/src/Config.res +94 -156
- package/src/Config.res.mjs +97 -60
- package/src/Core.res +0 -32
- package/src/Env.res.mjs +2 -1
- package/src/Envio.res +0 -94
- package/src/EventConfigBuilder.res +25 -63
- package/src/EventConfigBuilder.res.mjs +8 -37
- package/src/HandlerLoader.res +1 -12
- package/src/HandlerLoader.res.mjs +1 -6
- package/src/Internal.res +0 -38
- package/src/Main.res +3 -53
- package/src/Main.res.mjs +2 -34
- package/src/Persistence.res +17 -2
- package/src/Persistence.res.mjs +14 -2
- package/src/SimulateItems.res +10 -23
- package/src/SimulateItems.res.mjs +6 -21
- package/src/bindings/ClickHouse.res +6 -2
- package/src/bindings/ClickHouse.res.mjs +3 -2
- package/src/sources/EventRouter.res +0 -65
- package/src/sources/EventRouter.res.mjs +0 -43
- package/src/sources/HyperSyncClient.res +157 -30
- package/src/sources/HyperSyncClient.res.mjs +6 -20
- package/src/sources/HyperSyncSource.res +8 -5
- package/src/sources/HyperSyncSource.res.mjs +8 -1
- package/src/sources/RpcSource.res.mjs +1 -1
- package/src/sources/Svm.res +2 -2
- package/src/sources/Svm.res.mjs +2 -3
- package/src/tui/Tui.res +2 -9
- package/src/tui/Tui.res.mjs +4 -19
- package/src/tui/components/TuiData.res +0 -3
- package/svm.schema.json +4 -345
- package/src/SvmTypes.res +0 -9
- package/src/SvmTypes.res.mjs +0 -14
- package/src/sources/HyperSyncSolanaClient.res +0 -227
- package/src/sources/HyperSyncSolanaClient.res.mjs +0 -25
- package/src/sources/HyperSyncSolanaSource.res +0 -515
- package/src/sources/HyperSyncSolanaSource.res.mjs +0 -441
package/src/Config.res
CHANGED
|
@@ -30,7 +30,7 @@ type evmRpcConfig = {
|
|
|
30
30
|
type sourceConfig =
|
|
31
31
|
| EvmSourceConfig({hypersync: option<string>, rpcs: array<evmRpcConfig>})
|
|
32
32
|
| FuelSourceConfig({hypersync: string})
|
|
33
|
-
| SvmSourceConfig({
|
|
33
|
+
| SvmSourceConfig({rpc: string})
|
|
34
34
|
// For tests: pass custom sources directly
|
|
35
35
|
| CustomSources(array<Source.t>)
|
|
36
36
|
|
|
@@ -201,38 +201,6 @@ let publicConfigChainSchema = S.schema(s =>
|
|
|
201
201
|
}
|
|
202
202
|
)
|
|
203
203
|
|
|
204
|
-
let svmEventDescriptorSchema = S.schema(s =>
|
|
205
|
-
{
|
|
206
|
-
"discriminator": s.matches(S.option(S.string)),
|
|
207
|
-
"discriminatorByteLen": s.matches(S.int),
|
|
208
|
-
"includeTransaction": s.matches(S.bool),
|
|
209
|
-
"includeLogs": s.matches(S.bool),
|
|
210
|
-
"accountFilters": s.matches(
|
|
211
|
-
S.option(
|
|
212
|
-
S.array(
|
|
213
|
-
S.schema(
|
|
214
|
-
s => {
|
|
215
|
-
"position": s.matches(S.int),
|
|
216
|
-
"values": s.matches(S.array(S.string)),
|
|
217
|
-
},
|
|
218
|
-
),
|
|
219
|
-
),
|
|
220
|
-
),
|
|
221
|
-
),
|
|
222
|
-
"isInner": s.matches(S.option(S.bool)),
|
|
223
|
-
"accounts": s.matches(S.option(S.array(S.string))),
|
|
224
|
-
"args": s.matches(S.option(S.json(~validate=false))),
|
|
225
|
-
}
|
|
226
|
-
)
|
|
227
|
-
|
|
228
|
-
let svmAbiSchema = S.schema(s =>
|
|
229
|
-
{
|
|
230
|
-
"programId": s.matches(S.string),
|
|
231
|
-
"definedTypes": s.matches(S.json(~validate=false)),
|
|
232
|
-
"source": s.matches(S.string),
|
|
233
|
-
}
|
|
234
|
-
)
|
|
235
|
-
|
|
236
204
|
let contractEventItemSchema = S.schema(s =>
|
|
237
205
|
{
|
|
238
206
|
"event": s.matches(S.string),
|
|
@@ -242,7 +210,6 @@ let contractEventItemSchema = S.schema(s =>
|
|
|
242
210
|
"kind": s.matches(S.option(S.string)),
|
|
243
211
|
"blockFields": s.matches(S.option(S.array(Internal.evmBlockFieldSchema))),
|
|
244
212
|
"transactionFields": s.matches(S.option(S.array(Internal.evmTransactionFieldSchema))),
|
|
245
|
-
"svm": s.matches(S.option(svmEventDescriptorSchema)),
|
|
246
213
|
}
|
|
247
214
|
)
|
|
248
215
|
|
|
@@ -252,8 +219,6 @@ let contractConfigSchema = S.schema(s =>
|
|
|
252
219
|
"handler": s.matches(S.option(S.string)),
|
|
253
220
|
// EVM-specific: event signatures for HyperSync queries
|
|
254
221
|
"events": s.matches(S.option(S.array(contractEventItemSchema))),
|
|
255
|
-
// SVM-only: program-level Borsh schema (defined-types registry, source).
|
|
256
|
-
"svmAbi": s.matches(S.option(svmAbiSchema)),
|
|
257
222
|
}
|
|
258
223
|
)
|
|
259
224
|
|
|
@@ -261,10 +226,6 @@ let publicConfigEcosystemSchema = S.schema(s =>
|
|
|
261
226
|
{
|
|
262
227
|
"chains": s.matches(S.dict(publicConfigChainSchema)),
|
|
263
228
|
"contracts": s.matches(S.option(S.dict(contractConfigSchema))),
|
|
264
|
-
// SVM-only alias: programs are the SVM analog of EVM/Fuel contracts.
|
|
265
|
-
// Parsed via the same `contractConfigSchema` and read in `fromPublic`'s
|
|
266
|
-
// `publicContractsConfig` switch.
|
|
267
|
-
"programs": s.matches(S.option(S.dict(contractConfigSchema))),
|
|
268
229
|
}
|
|
269
230
|
)
|
|
270
231
|
|
|
@@ -562,19 +523,10 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
562
523
|
| None => false
|
|
563
524
|
}
|
|
564
525
|
|
|
565
|
-
// Parse contract configs (ABIs, events, handlers)
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
let publicContractsConfig = switch (
|
|
570
|
-
ecosystemName,
|
|
571
|
-
publicConfig["evm"],
|
|
572
|
-
publicConfig["fuel"],
|
|
573
|
-
publicConfig["svm"],
|
|
574
|
-
) {
|
|
575
|
-
| (Ecosystem.Evm, Some(evm), _, _) => evm["contracts"]
|
|
576
|
-
| (Ecosystem.Fuel, _, Some(fuel), _) => fuel["contracts"]
|
|
577
|
-
| (Ecosystem.Svm, _, _, Some(svm)) => svm["programs"]
|
|
526
|
+
// Parse contract configs (ABIs, events, handlers)
|
|
527
|
+
let publicContractsConfig = switch (ecosystemName, publicConfig["evm"], publicConfig["fuel"]) {
|
|
528
|
+
| (Ecosystem.Evm, Some(evm), _) => evm["contracts"]
|
|
529
|
+
| (Ecosystem.Fuel, _, Some(fuel)) => fuel["contracts"]
|
|
578
530
|
| _ => None
|
|
579
531
|
}
|
|
580
532
|
|
|
@@ -597,7 +549,6 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
597
549
|
"abi": EvmTypes.Abi.t,
|
|
598
550
|
"eventSignatures": array<string>,
|
|
599
551
|
"events": option<array<_>>,
|
|
600
|
-
"svmAbi": option<{"programId": string, "definedTypes": JSON.t, "source": string}>,
|
|
601
552
|
}> = Dict.make()
|
|
602
553
|
switch publicContractsConfig {
|
|
603
554
|
| Some(contractsDict) =>
|
|
@@ -610,40 +561,21 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
610
561
|
| Some(events) => events->Array.map(eventItem => eventItem["event"])
|
|
611
562
|
| None => []
|
|
612
563
|
}
|
|
613
|
-
let widened =
|
|
614
|
-
contractConfig->(
|
|
615
|
-
Utils.magic: _ => {
|
|
616
|
-
"svmAbi": option<{"programId": string, "definedTypes": JSON.t, "source": string}>,
|
|
617
|
-
}
|
|
618
|
-
)
|
|
619
564
|
contractDataByName->Dict.set(
|
|
620
565
|
capitalizedName,
|
|
621
|
-
{
|
|
622
|
-
"abi": abi,
|
|
623
|
-
"eventSignatures": eventSignatures,
|
|
624
|
-
"events": contractConfig["events"],
|
|
625
|
-
"svmAbi": widened["svmAbi"],
|
|
626
|
-
},
|
|
566
|
+
{"abi": abi, "eventSignatures": eventSignatures, "events": contractConfig["events"]},
|
|
627
567
|
)
|
|
628
568
|
})
|
|
629
569
|
| None => ()
|
|
630
570
|
}
|
|
631
571
|
|
|
632
|
-
// Build event configs for a contract from JSON event items
|
|
633
|
-
//
|
|
634
|
-
// `~addresses` is the chain-side address list. For SVM programs it's the
|
|
635
|
-
// single base58 program_id — wired onto each instruction's event config so
|
|
636
|
-
// the source can build `(programId, discriminator)`-keyed InstructionSelections.
|
|
637
|
-
// EVM and Fuel ignore it (the address lives in `ChainContract.addresses` and
|
|
638
|
-
// is looked up at dispatch time, not stamped on the event).
|
|
572
|
+
// Build event configs for a contract from JSON event items
|
|
639
573
|
let buildContractEvents = (
|
|
640
574
|
~contractName,
|
|
641
575
|
~events: option<array<_>>,
|
|
642
576
|
~abi,
|
|
643
577
|
~chainId: int,
|
|
644
578
|
~startBlock: option<int>,
|
|
645
|
-
~addresses: array<string>,
|
|
646
|
-
~svmDefinedTypes: JSON.t=JSON.Null,
|
|
647
579
|
) => {
|
|
648
580
|
switch events {
|
|
649
581
|
| None => []
|
|
@@ -674,65 +606,6 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
674
606
|
`Fuel event ${contractName}.${eventName} is missing "kind" in internal config`,
|
|
675
607
|
)
|
|
676
608
|
}
|
|
677
|
-
| Ecosystem.Svm =>
|
|
678
|
-
let programId = switch addresses {
|
|
679
|
-
| [pid] => pid->SvmTypes.Pubkey.fromStringUnsafe
|
|
680
|
-
| [] =>
|
|
681
|
-
JsError.throwWithMessage(
|
|
682
|
-
`SVM program ${contractName} on chain ${chainId->Int.toString} is missing a program_id`,
|
|
683
|
-
)
|
|
684
|
-
| _ =>
|
|
685
|
-
JsError.throwWithMessage(
|
|
686
|
-
`SVM program ${contractName} on chain ${chainId->Int.toString} has multiple addresses; a program is uniquely identified by a single program_id`,
|
|
687
|
-
)
|
|
688
|
-
}
|
|
689
|
-
let widenedEventItem =
|
|
690
|
-
eventItem->(
|
|
691
|
-
Utils.magic: _ => {
|
|
692
|
-
"svm": option<{
|
|
693
|
-
"discriminator": option<string>,
|
|
694
|
-
"discriminatorByteLen": int,
|
|
695
|
-
"includeTransaction": bool,
|
|
696
|
-
"includeLogs": bool,
|
|
697
|
-
"accountFilters": option<
|
|
698
|
-
array<{"position": int, "values": array<string>}>,
|
|
699
|
-
>,
|
|
700
|
-
"isInner": option<bool>,
|
|
701
|
-
"accounts": option<array<string>>,
|
|
702
|
-
"args": option<JSON.t>,
|
|
703
|
-
}>,
|
|
704
|
-
}
|
|
705
|
-
)
|
|
706
|
-
let svm = switch widenedEventItem["svm"] {
|
|
707
|
-
| Some(s) => s
|
|
708
|
-
| None =>
|
|
709
|
-
JsError.throwWithMessage(
|
|
710
|
-
`SVM instruction ${contractName}.${eventName} is missing the "svm" descriptor in internal config`,
|
|
711
|
-
)
|
|
712
|
-
}
|
|
713
|
-
let accountFilters =
|
|
714
|
-
(svm["accountFilters"]->Option.getOr([]))->Array.map(af => {
|
|
715
|
-
Internal.position: af["position"],
|
|
716
|
-
values: af["values"]->SvmTypes.Pubkey.fromStringsUnsafe,
|
|
717
|
-
})
|
|
718
|
-
(EventConfigBuilder.buildSvmInstructionEventConfig(
|
|
719
|
-
~contractName,
|
|
720
|
-
~instructionName=eventName,
|
|
721
|
-
~programId,
|
|
722
|
-
~discriminator=svm["discriminator"],
|
|
723
|
-
~discriminatorByteLen=svm["discriminatorByteLen"],
|
|
724
|
-
~includeTransaction=svm["includeTransaction"],
|
|
725
|
-
~includeLogs=svm["includeLogs"],
|
|
726
|
-
~accountFilters,
|
|
727
|
-
~isInner=svm["isInner"],
|
|
728
|
-
~isWildcard=false,
|
|
729
|
-
~handler=None,
|
|
730
|
-
~contractRegister=None,
|
|
731
|
-
~accounts=svm["accounts"]->Option.getOr([]),
|
|
732
|
-
~args=svm["args"]->Option.getOr(JSON.Null),
|
|
733
|
-
~definedTypes=svmDefinedTypes,
|
|
734
|
-
~startBlock?,
|
|
735
|
-
) :> Internal.eventConfig)
|
|
736
609
|
| _ =>
|
|
737
610
|
(EventConfigBuilder.buildEvmEventConfig(
|
|
738
611
|
~contractName,
|
|
@@ -792,11 +665,11 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
792
665
|
->Dict.toArray
|
|
793
666
|
->Array.map(((capitalizedName, contractData)) => {
|
|
794
667
|
let chainContract = chainContracts->Dict.get(capitalizedName)
|
|
795
|
-
let
|
|
668
|
+
let addresses =
|
|
796
669
|
chainContract
|
|
797
670
|
->Option.flatMap(cc => cc["addresses"])
|
|
798
671
|
->Option.getOr([])
|
|
799
|
-
|
|
672
|
+
->Array.map(parseAddress)
|
|
800
673
|
let startBlock = chainContract->Option.flatMap(cc => cc["startBlock"])
|
|
801
674
|
|
|
802
675
|
// Build event configs from JSON (field selections resolved inline)
|
|
@@ -810,10 +683,6 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
810
683
|
~abi=contractData["abi"],
|
|
811
684
|
~chainId,
|
|
812
685
|
~startBlock,
|
|
813
|
-
~addresses=rawAddresses,
|
|
814
|
-
~svmDefinedTypes=contractData["svmAbi"]
|
|
815
|
-
->Option.map(a => a["definedTypes"])
|
|
816
|
-
->Option.getOr(JSON.Null),
|
|
817
686
|
)
|
|
818
687
|
|
|
819
688
|
{
|
|
@@ -879,11 +748,7 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
879
748
|
}
|
|
880
749
|
| Ecosystem.Svm =>
|
|
881
750
|
switch publicChainConfig["rpc"] {
|
|
882
|
-
| Some(rpc) =>
|
|
883
|
-
SvmSourceConfig({
|
|
884
|
-
hypersync: publicChainConfig["hypersync"],
|
|
885
|
-
rpc,
|
|
886
|
-
})
|
|
751
|
+
| Some(rpc) => SvmSourceConfig({rpc: rpc})
|
|
887
752
|
| None => JsError.throwWithMessage(`Chain ${chainName} is missing rpc endpoint in config`)
|
|
888
753
|
}
|
|
889
754
|
}
|
|
@@ -1092,11 +957,18 @@ let rec canonicalJson = (json: JSON.t): JSON.t =>
|
|
|
1092
957
|
}
|
|
1093
958
|
|
|
1094
959
|
// Returns dotted leaf paths (`a.b[i].c`) where `stored` differs from
|
|
1095
|
-
// `current
|
|
960
|
+
// `current`, restricted to the highest-priority top-level tier with any
|
|
961
|
+
// diff. Tiers in order: version → name → storage → ecosystem
|
|
962
|
+
// (evm/fuel/svm) → entities → other top-level keys. The first tier
|
|
963
|
+
// containing a diff is the only one rendered; lower tiers are silenced
|
|
964
|
+
// so a single noisy section doesn't bury the actionable change.
|
|
1096
965
|
let diffPaths = (~stored: JSON.t, ~current: JSON.t): array<string> => {
|
|
966
|
+
let canonEq = (a: JSON.t, b: JSON.t) =>
|
|
967
|
+
JSON.stringify(canonicalJson(a)) === JSON.stringify(canonicalJson(b))
|
|
968
|
+
|
|
1097
969
|
let acc = []
|
|
1098
970
|
let rec go = (s: JSON.t, c: JSON.t, prefix: string) => {
|
|
1099
|
-
if
|
|
971
|
+
if canonEq(s, c) {
|
|
1100
972
|
()
|
|
1101
973
|
} else {
|
|
1102
974
|
switch (s, c) {
|
|
@@ -1122,25 +994,91 @@ let diffPaths = (~stored: JSON.t, ~current: JSON.t): array<string> => {
|
|
|
1122
994
|
| (Some(sv), Some(cv)) => go(sv, cv, p)
|
|
1123
995
|
}
|
|
1124
996
|
}
|
|
1125
|
-
| _ =>
|
|
1126
|
-
// Type mismatch or scalar diff
|
|
1127
|
-
acc->Array.push(prefix === "" ? "<root>" : prefix)->ignore
|
|
997
|
+
| _ => acc->Array.push(prefix === "" ? "<root>" : prefix)->ignore
|
|
1128
998
|
}
|
|
1129
999
|
}
|
|
1130
1000
|
}
|
|
1131
|
-
|
|
1001
|
+
|
|
1002
|
+
let getTopKey = (j: JSON.t, k: string) =>
|
|
1003
|
+
switch j {
|
|
1004
|
+
| Object(d) => d->Dict.get(k)
|
|
1005
|
+
| _ => None
|
|
1006
|
+
}
|
|
1007
|
+
let topKeyDiffers = (k: string) =>
|
|
1008
|
+
switch (getTopKey(stored, k), getTopKey(current, k)) {
|
|
1009
|
+
| (None, None) => false
|
|
1010
|
+
| (None, _) | (_, None) => true
|
|
1011
|
+
| (Some(s), Some(c)) => !canonEq(s, c)
|
|
1012
|
+
}
|
|
1013
|
+
let runTier = (keys: array<string>) =>
|
|
1014
|
+
keys->Array.forEach(k =>
|
|
1015
|
+
switch (getTopKey(stored, k), getTopKey(current, k)) {
|
|
1016
|
+
| (None, None) => ()
|
|
1017
|
+
| (None, _) | (_, None) => acc->Array.push(k)->ignore
|
|
1018
|
+
| (Some(s), Some(c)) => go(s, c, k)
|
|
1019
|
+
}
|
|
1020
|
+
)
|
|
1021
|
+
|
|
1022
|
+
switch (stored, current) {
|
|
1023
|
+
| (Object(sObj), Object(cObj)) =>
|
|
1024
|
+
let tiers = [["version"], ["name"], ["storage"], ["evm", "fuel", "svm"], ["entities"]]
|
|
1025
|
+
let firstHit = tiers->Array.reduce(None, (acc, tier) =>
|
|
1026
|
+
switch acc {
|
|
1027
|
+
| Some(_) => acc
|
|
1028
|
+
| None =>
|
|
1029
|
+
switch tier->Array.filter(topKeyDiffers) {
|
|
1030
|
+
| [] => None
|
|
1031
|
+
| hits => Some(hits)
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
)
|
|
1035
|
+
switch firstHit {
|
|
1036
|
+
| Some(hits) => runTier(hits)
|
|
1037
|
+
| None =>
|
|
1038
|
+
let knownSet = Utils.Set.fromArray(tiers->Belt.Array.concatMany)
|
|
1039
|
+
let extras =
|
|
1040
|
+
Utils.Set.fromArray(Array.concat(sObj->Dict.keysToArray, cObj->Dict.keysToArray))
|
|
1041
|
+
->Utils.Set.toArray
|
|
1042
|
+
->Array.filter(k => !(knownSet->Utils.Set.has(k)))
|
|
1043
|
+
->Array.toSorted(String.compare)
|
|
1044
|
+
->Array.filter(topKeyDiffers)
|
|
1045
|
+
runTier(extras)
|
|
1046
|
+
}
|
|
1047
|
+
| _ => go(stored, current, "")
|
|
1048
|
+
}
|
|
1132
1049
|
acc
|
|
1133
1050
|
}
|
|
1134
1051
|
|
|
1135
1052
|
// Throws an `incompatible config` error listing each path in `changedPaths`,
|
|
1136
|
-
// plus the
|
|
1137
|
-
// (
|
|
1138
|
-
//
|
|
1139
|
-
|
|
1053
|
+
// plus the remediation options. `~resetCommand` is rendered as-is for
|
|
1054
|
+
// option 2 (the wipe-and-redo). `~runCommand` controls option 3 (parallel
|
|
1055
|
+
// indexer recipe): when `None`, option 3 is omitted — the migrate flow
|
|
1056
|
+
// uses this because running a second indexer doesn't apply.
|
|
1057
|
+
// `~hasClickhouse` adds the extra env line so users running both
|
|
1058
|
+
// Postgres and Clickhouse get a complete override.
|
|
1059
|
+
let throwIfIncompatible = (
|
|
1060
|
+
changedPaths: array<string>,
|
|
1061
|
+
~resetCommand: string,
|
|
1062
|
+
~runCommand: option<string>,
|
|
1063
|
+
~hasClickhouse: bool,
|
|
1064
|
+
) => {
|
|
1140
1065
|
if changedPaths->Array.length > 0 {
|
|
1141
1066
|
let bullets = changedPaths->Array.map(p => ` - ${p}`)->Array.joinUnsafe("\n")
|
|
1067
|
+
let option1 = "Revert the changes above"
|
|
1068
|
+
let padTo = (s, col) => s ++ " "->String.repeat(Math.Int.max(col - String.length(s), 1))
|
|
1069
|
+
let col = Math.Int.max(String.length(option1), String.length(resetCommand)) + 2
|
|
1070
|
+
let option3 = switch runCommand {
|
|
1071
|
+
| None => ""
|
|
1072
|
+
| Some(cmd) =>
|
|
1073
|
+
let clickhouseLine = hasClickhouse ? " ENVIO_CLICKHOUSE_DATABASE=<new_db> \\\n" : ""
|
|
1074
|
+
`\n 3. Run a second indexer alongside this one — keep both datasets:\n ENVIO_PG_SCHEMA=<new_schema> \\\n${clickhouseLine} ENVIO_INDEXER_PORT=<new_port> \\\n ${cmd}`
|
|
1075
|
+
}
|
|
1142
1076
|
JsError.throwWithMessage(
|
|
1143
|
-
`The following config changes are incompatible with the existing indexer data:\n\n${bullets}\n\nPick one:\n
|
|
1077
|
+
`The following config changes are incompatible with the existing indexer data:\n\n${bullets}\n\nPick one:\n 1. ${option1->padTo(
|
|
1078
|
+
col,
|
|
1079
|
+
)}# resume indexing where it left off\n 2. ${resetCommand->padTo(
|
|
1080
|
+
col,
|
|
1081
|
+
)}# delete all indexed data and start over${option3}`,
|
|
1144
1082
|
)
|
|
1145
1083
|
}
|
|
1146
1084
|
}
|
package/src/Config.res.mjs
CHANGED
|
@@ -113,26 +113,6 @@ let publicConfigChainSchema = S$RescriptSchema.schema(s => ({
|
|
|
113
113
|
contracts: s.m(S$RescriptSchema.option(S$RescriptSchema.dict(chainContractSchema)))
|
|
114
114
|
}));
|
|
115
115
|
|
|
116
|
-
let svmEventDescriptorSchema = S$RescriptSchema.schema(s => ({
|
|
117
|
-
discriminator: s.m(S$RescriptSchema.option(S$RescriptSchema.string)),
|
|
118
|
-
discriminatorByteLen: s.m(S$RescriptSchema.int),
|
|
119
|
-
includeTransaction: s.m(S$RescriptSchema.bool),
|
|
120
|
-
includeLogs: s.m(S$RescriptSchema.bool),
|
|
121
|
-
accountFilters: s.m(S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.schema(s => ({
|
|
122
|
-
position: s.m(S$RescriptSchema.int),
|
|
123
|
-
values: s.m(S$RescriptSchema.array(S$RescriptSchema.string))
|
|
124
|
-
}))))),
|
|
125
|
-
isInner: s.m(S$RescriptSchema.option(S$RescriptSchema.bool)),
|
|
126
|
-
accounts: s.m(S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.string))),
|
|
127
|
-
args: s.m(S$RescriptSchema.option(S$RescriptSchema.json(false)))
|
|
128
|
-
}));
|
|
129
|
-
|
|
130
|
-
let svmAbiSchema = S$RescriptSchema.schema(s => ({
|
|
131
|
-
programId: s.m(S$RescriptSchema.string),
|
|
132
|
-
definedTypes: s.m(S$RescriptSchema.json(false)),
|
|
133
|
-
source: s.m(S$RescriptSchema.string)
|
|
134
|
-
}));
|
|
135
|
-
|
|
136
116
|
let contractEventItemSchema = S$RescriptSchema.schema(s => ({
|
|
137
117
|
event: s.m(S$RescriptSchema.string),
|
|
138
118
|
name: s.m(S$RescriptSchema.string),
|
|
@@ -140,21 +120,18 @@ let contractEventItemSchema = S$RescriptSchema.schema(s => ({
|
|
|
140
120
|
params: s.m(S$RescriptSchema.option(S$RescriptSchema.array(EventConfigBuilder.eventParamSchema))),
|
|
141
121
|
kind: s.m(S$RescriptSchema.option(S$RescriptSchema.string)),
|
|
142
122
|
blockFields: s.m(S$RescriptSchema.option(S$RescriptSchema.array(Internal.evmBlockFieldSchema))),
|
|
143
|
-
transactionFields: s.m(S$RescriptSchema.option(S$RescriptSchema.array(Internal.evmTransactionFieldSchema)))
|
|
144
|
-
svm: s.m(S$RescriptSchema.option(svmEventDescriptorSchema))
|
|
123
|
+
transactionFields: s.m(S$RescriptSchema.option(S$RescriptSchema.array(Internal.evmTransactionFieldSchema)))
|
|
145
124
|
}));
|
|
146
125
|
|
|
147
126
|
let contractConfigSchema = S$RescriptSchema.schema(s => ({
|
|
148
127
|
abi: s.m(S$RescriptSchema.json(false)),
|
|
149
128
|
handler: s.m(S$RescriptSchema.option(S$RescriptSchema.string)),
|
|
150
|
-
events: s.m(S$RescriptSchema.option(S$RescriptSchema.array(contractEventItemSchema)))
|
|
151
|
-
svmAbi: s.m(S$RescriptSchema.option(svmAbiSchema))
|
|
129
|
+
events: s.m(S$RescriptSchema.option(S$RescriptSchema.array(contractEventItemSchema)))
|
|
152
130
|
}));
|
|
153
131
|
|
|
154
132
|
let publicConfigEcosystemSchema = S$RescriptSchema.schema(s => ({
|
|
155
133
|
chains: s.m(S$RescriptSchema.dict(publicConfigChainSchema)),
|
|
156
|
-
contracts: s.m(S$RescriptSchema.option(S$RescriptSchema.dict(contractConfigSchema)))
|
|
157
|
-
programs: s.m(S$RescriptSchema.option(S$RescriptSchema.dict(contractConfigSchema)))
|
|
134
|
+
contracts: s.m(S$RescriptSchema.option(S$RescriptSchema.dict(contractConfigSchema)))
|
|
158
135
|
}));
|
|
159
136
|
|
|
160
137
|
let publicConfigEvmSchema = S$RescriptSchema.schema(s => ({
|
|
@@ -442,7 +419,6 @@ function fromPublic(publicConfigJson) {
|
|
|
442
419
|
let lowercaseAddresses = evm !== undefined ? Stdlib_Option.getOr(Primitive_option.valFromOption(evm).addressFormat, "checksum") === "lowercase" : false;
|
|
443
420
|
let match$4 = publicConfig.evm;
|
|
444
421
|
let match$5 = publicConfig.fuel;
|
|
445
|
-
let match$6 = publicConfig.svm;
|
|
446
422
|
let publicContractsConfig;
|
|
447
423
|
switch (ecosystemName) {
|
|
448
424
|
case "evm" :
|
|
@@ -452,25 +428,25 @@ function fromPublic(publicConfigJson) {
|
|
|
452
428
|
publicContractsConfig = match$5 !== undefined ? Primitive_option.valFromOption(match$5).contracts : undefined;
|
|
453
429
|
break;
|
|
454
430
|
case "svm" :
|
|
455
|
-
publicContractsConfig =
|
|
431
|
+
publicContractsConfig = undefined;
|
|
456
432
|
break;
|
|
457
433
|
}
|
|
458
434
|
let evm$1 = publicConfig.evm;
|
|
459
|
-
let match$
|
|
435
|
+
let match$6;
|
|
460
436
|
if (evm$1 !== undefined) {
|
|
461
437
|
let evm$2 = Primitive_option.valFromOption(evm$1);
|
|
462
|
-
match$
|
|
438
|
+
match$6 = [
|
|
463
439
|
new Set(EventConfigBuilder.alwaysIncludedBlockFields.concat(Stdlib_Option.getOr(evm$2.globalBlockFields, []))),
|
|
464
440
|
new Set(Stdlib_Option.getOr(evm$2.globalTransactionFields, []))
|
|
465
441
|
];
|
|
466
442
|
} else {
|
|
467
|
-
match$
|
|
443
|
+
match$6 = [
|
|
468
444
|
new Set(EventConfigBuilder.alwaysIncludedBlockFields),
|
|
469
445
|
new Set()
|
|
470
446
|
];
|
|
471
447
|
}
|
|
472
|
-
let globalTransactionFieldsSet = match$
|
|
473
|
-
let globalBlockFieldsSet = match$
|
|
448
|
+
let globalTransactionFieldsSet = match$6[1];
|
|
449
|
+
let globalBlockFieldsSet = match$6[0];
|
|
474
450
|
let contractDataByName = {};
|
|
475
451
|
if (publicContractsConfig !== undefined) {
|
|
476
452
|
Object.entries(publicContractsConfig).forEach(param => {
|
|
@@ -482,13 +458,11 @@ function fromPublic(publicConfigJson) {
|
|
|
482
458
|
contractDataByName[capitalizedName] = {
|
|
483
459
|
abi: abi,
|
|
484
460
|
eventSignatures: eventSignatures,
|
|
485
|
-
events: contractConfig.events
|
|
486
|
-
svmAbi: contractConfig.svmAbi
|
|
461
|
+
events: contractConfig.events
|
|
487
462
|
};
|
|
488
463
|
});
|
|
489
464
|
}
|
|
490
|
-
let buildContractEvents = (contractName, events, abi, chainId, startBlock
|
|
491
|
-
let svmDefinedTypes = svmDefinedTypesOpt !== undefined ? svmDefinedTypesOpt : null;
|
|
465
|
+
let buildContractEvents = (contractName, events, abi, chainId, startBlock) => {
|
|
492
466
|
if (events !== undefined) {
|
|
493
467
|
return events.map(eventItem => {
|
|
494
468
|
let eventName = eventItem.name;
|
|
@@ -496,27 +470,17 @@ function fromPublic(publicConfigJson) {
|
|
|
496
470
|
let params = Stdlib_Option.getOr(eventItem.params, []);
|
|
497
471
|
let kind = eventItem.kind;
|
|
498
472
|
switch (ecosystemName) {
|
|
499
|
-
case "evm" :
|
|
500
|
-
return EventConfigBuilder.buildEvmEventConfig(contractName, eventName, sighash, params, false, undefined, undefined, undefined, chainId, ecosystem.onEventBlockFilterSchema, eventItem.blockFields, eventItem.transactionFields, startBlock, Primitive_option.some(globalBlockFieldsSet), Primitive_option.some(globalTransactionFieldsSet));
|
|
501
473
|
case "fuel" :
|
|
502
474
|
if (kind !== undefined) {
|
|
503
475
|
return EventConfigBuilder.buildFuelEventConfig(contractName, eventName, kind, sighash, abi, false, undefined, undefined, startBlock);
|
|
504
476
|
} else {
|
|
505
477
|
return Stdlib_JsError.throwWithMessage(`Fuel event ` + contractName + `.` + eventName + ` is missing "kind" in internal config`);
|
|
506
478
|
}
|
|
479
|
+
case "evm" :
|
|
507
480
|
case "svm" :
|
|
508
|
-
|
|
509
|
-
let programId = len !== 1 ? (
|
|
510
|
-
len !== 0 ? Stdlib_JsError.throwWithMessage(`SVM program ` + contractName + ` on chain ` + chainId.toString() + ` has multiple addresses; a program is uniquely identified by a single program_id`) : Stdlib_JsError.throwWithMessage(`SVM program ` + contractName + ` on chain ` + chainId.toString() + ` is missing a program_id`)
|
|
511
|
-
) : addresses[0];
|
|
512
|
-
let s = eventItem.svm;
|
|
513
|
-
let svm = s !== undefined ? Primitive_option.valFromOption(s) : Stdlib_JsError.throwWithMessage(`SVM instruction ` + contractName + `.` + eventName + ` is missing the "svm" descriptor in internal config`);
|
|
514
|
-
let accountFilters = Stdlib_Option.getOr(svm.accountFilters, []).map(af => ({
|
|
515
|
-
position: af.position,
|
|
516
|
-
values: af.values
|
|
517
|
-
}));
|
|
518
|
-
return EventConfigBuilder.buildSvmInstructionEventConfig(contractName, eventName, programId, svm.discriminator, svm.discriminatorByteLen, svm.includeTransaction, svm.includeLogs, accountFilters, svm.isInner, false, undefined, undefined, Stdlib_Option.getOr(svm.accounts, []), Stdlib_Option.getOr(svm.args, null), svmDefinedTypes, startBlock);
|
|
481
|
+
break;
|
|
519
482
|
}
|
|
483
|
+
return EventConfigBuilder.buildEvmEventConfig(contractName, eventName, sighash, params, false, undefined, undefined, undefined, chainId, ecosystem.onEventBlockFilterSchema, eventItem.blockFields, eventItem.transactionFields, startBlock, Primitive_option.some(globalBlockFieldsSet), Primitive_option.some(globalTransactionFieldsSet));
|
|
520
484
|
});
|
|
521
485
|
} else {
|
|
522
486
|
return [];
|
|
@@ -553,10 +517,9 @@ function fromPublic(publicConfigJson) {
|
|
|
553
517
|
let contractData = param[1];
|
|
554
518
|
let capitalizedName = param[0];
|
|
555
519
|
let chainContract = chainContracts[capitalizedName];
|
|
556
|
-
let
|
|
557
|
-
let addresses = rawAddresses.map(parseAddress);
|
|
520
|
+
let addresses = Stdlib_Option.getOr(Stdlib_Option.flatMap(chainContract, cc => cc.addresses), []).map(parseAddress);
|
|
558
521
|
let startBlock = Stdlib_Option.flatMap(chainContract, cc => cc.startBlock);
|
|
559
|
-
let events = buildContractEvents(capitalizedName, contractData.events, contractData.abi, chainId, startBlock
|
|
522
|
+
let events = buildContractEvents(capitalizedName, contractData.events, contractData.abi, chainId, startBlock);
|
|
560
523
|
return {
|
|
561
524
|
name: capitalizedName,
|
|
562
525
|
abi: contractData.abi,
|
|
@@ -613,7 +576,6 @@ function fromPublic(publicConfigJson) {
|
|
|
613
576
|
let rpc = publicChainConfig.rpc;
|
|
614
577
|
sourceConfig = rpc !== undefined ? ({
|
|
615
578
|
TAG: "SvmSourceConfig",
|
|
616
|
-
hypersync: publicChainConfig.hypersync,
|
|
617
579
|
rpc: rpc
|
|
618
580
|
}) : Stdlib_JsError.throwWithMessage(`Chain ` + chainName + ` is missing rpc endpoint in config`);
|
|
619
581
|
break;
|
|
@@ -818,9 +780,10 @@ function canonicalJson(json) {
|
|
|
818
780
|
}
|
|
819
781
|
|
|
820
782
|
function diffPaths(stored, current) {
|
|
783
|
+
let canonEq = (a, b) => JSON.stringify(canonicalJson(a)) === JSON.stringify(canonicalJson(b));
|
|
821
784
|
let acc = [];
|
|
822
785
|
let go = (s, c, prefix) => {
|
|
823
|
-
if (
|
|
786
|
+
if (canonEq(s, c)) {
|
|
824
787
|
return;
|
|
825
788
|
}
|
|
826
789
|
if (Array.isArray(s)) {
|
|
@@ -868,16 +831,92 @@ function diffPaths(stored, current) {
|
|
|
868
831
|
}
|
|
869
832
|
acc.push(prefix === "" ? "<root>" : prefix);
|
|
870
833
|
};
|
|
871
|
-
|
|
834
|
+
let getTopKey = (j, k) => {
|
|
835
|
+
if (typeof j === "object" && j !== null && !Array.isArray(j)) {
|
|
836
|
+
return j[k];
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
let topKeyDiffers = k => {
|
|
840
|
+
let match = getTopKey(stored, k);
|
|
841
|
+
let match$1 = getTopKey(current, k);
|
|
842
|
+
if (match !== undefined) {
|
|
843
|
+
if (match$1 !== undefined) {
|
|
844
|
+
return !canonEq(match, match$1);
|
|
845
|
+
} else {
|
|
846
|
+
return true;
|
|
847
|
+
}
|
|
848
|
+
} else {
|
|
849
|
+
return match$1 !== undefined;
|
|
850
|
+
}
|
|
851
|
+
};
|
|
852
|
+
let runTier = keys => {
|
|
853
|
+
keys.forEach(k => {
|
|
854
|
+
let match = getTopKey(stored, k);
|
|
855
|
+
let match$1 = getTopKey(current, k);
|
|
856
|
+
if (match !== undefined) {
|
|
857
|
+
if (match$1 !== undefined) {
|
|
858
|
+
return go(match, match$1, k);
|
|
859
|
+
} else {
|
|
860
|
+
acc.push(k);
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
} else if (match$1 !== undefined) {
|
|
864
|
+
acc.push(k);
|
|
865
|
+
return;
|
|
866
|
+
} else {
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
});
|
|
870
|
+
};
|
|
871
|
+
if (typeof stored === "object" && stored !== null && !Array.isArray(stored) && typeof current === "object" && current !== null && !Array.isArray(current)) {
|
|
872
|
+
let tiers = [
|
|
873
|
+
["version"],
|
|
874
|
+
["name"],
|
|
875
|
+
["storage"],
|
|
876
|
+
[
|
|
877
|
+
"evm",
|
|
878
|
+
"fuel",
|
|
879
|
+
"svm"
|
|
880
|
+
],
|
|
881
|
+
["entities"]
|
|
882
|
+
];
|
|
883
|
+
let firstHit = Stdlib_Array.reduce(tiers, undefined, (acc, tier) => {
|
|
884
|
+
if (acc !== undefined) {
|
|
885
|
+
return acc;
|
|
886
|
+
}
|
|
887
|
+
let hits = tier.filter(topKeyDiffers);
|
|
888
|
+
if (hits.length !== 0) {
|
|
889
|
+
return hits;
|
|
890
|
+
}
|
|
891
|
+
});
|
|
892
|
+
if (firstHit !== undefined) {
|
|
893
|
+
runTier(firstHit);
|
|
894
|
+
} else {
|
|
895
|
+
let knownSet = new Set(Belt_Array.concatMany(tiers));
|
|
896
|
+
runTier(Array.from(new Set(Object.keys(stored).concat(Object.keys(current)))).filter(k => !knownSet.has(k)).toSorted(Primitive_string.compare).filter(topKeyDiffers));
|
|
897
|
+
}
|
|
898
|
+
} else {
|
|
899
|
+
go(stored, current, "");
|
|
900
|
+
}
|
|
872
901
|
return acc;
|
|
873
902
|
}
|
|
874
903
|
|
|
875
|
-
function throwIfIncompatible(changedPaths, resetCommand) {
|
|
904
|
+
function throwIfIncompatible(changedPaths, resetCommand, runCommand, hasClickhouse) {
|
|
876
905
|
if (changedPaths.length === 0) {
|
|
877
906
|
return;
|
|
878
907
|
}
|
|
879
908
|
let bullets = changedPaths.map(p => ` - ` + p).join("\n");
|
|
880
|
-
|
|
909
|
+
let option1 = "Revert the changes above";
|
|
910
|
+
let padTo = (s, col) => s + " ".repeat(Math.max(col - s.length | 0, 1));
|
|
911
|
+
let col = Math.max(option1.length, resetCommand.length) + 2 | 0;
|
|
912
|
+
let option3;
|
|
913
|
+
if (runCommand !== undefined) {
|
|
914
|
+
let clickhouseLine = hasClickhouse ? " ENVIO_CLICKHOUSE_DATABASE=<new_db> \\\n" : "";
|
|
915
|
+
option3 = `\n 3. Run a second indexer alongside this one — keep both datasets:\n ENVIO_PG_SCHEMA=<new_schema> \\\n` + clickhouseLine + ` ENVIO_INDEXER_PORT=<new_port> \\\n ` + runCommand;
|
|
916
|
+
} else {
|
|
917
|
+
option3 = "";
|
|
918
|
+
}
|
|
919
|
+
Stdlib_JsError.throwWithMessage(`The following config changes are incompatible with the existing indexer data:\n\n` + bullets + `\n\nPick one:\n 1. ` + padTo(option1, col) + `# resume indexing where it left off\n 2. ` + padTo(resetCommand, col) + `# delete all indexed data and start over` + option3);
|
|
881
920
|
}
|
|
882
921
|
|
|
883
922
|
function loadWithoutRegistrations() {
|
|
@@ -900,8 +939,6 @@ export {
|
|
|
900
939
|
rpcConfigSchema,
|
|
901
940
|
chainContractSchema,
|
|
902
941
|
publicConfigChainSchema,
|
|
903
|
-
svmEventDescriptorSchema,
|
|
904
|
-
svmAbiSchema,
|
|
905
942
|
contractEventItemSchema,
|
|
906
943
|
contractConfigSchema,
|
|
907
944
|
publicConfigEcosystemSchema,
|