envio 3.8.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.
- package/README.md +2 -2
- package/evm.schema.json +7 -0
- package/fuel.schema.json +7 -0
- package/index.d.ts +433 -109
- package/index.js +10 -0
- package/package.json +6 -7
- package/src/AddressRows.res +121 -0
- package/src/AddressRows.res.mjs +143 -0
- package/src/Batch.res +11 -0
- package/src/Batch.res.mjs +2 -1
- package/src/Bin.res +47 -0
- package/src/Bin.res.mjs +34 -1
- package/src/ChainState.res +102 -100
- package/src/ChainState.res.mjs +81 -67
- package/src/ChainState.resi +28 -15
- package/src/Config.res +43 -122
- package/src/Config.res.mjs +33 -75
- package/src/ContractMapping.res +66 -0
- package/src/ContractMapping.res.mjs +73 -0
- package/src/Core.res +24 -0
- package/src/Core.res.mjs +4 -0
- package/src/Ecosystem.res +7 -2
- package/src/Env.res +128 -1
- package/src/Env.res.mjs +123 -1
- package/src/EventConfigBuilder.res +169 -28
- package/src/EventConfigBuilder.res.mjs +124 -17
- package/src/FetchState.res +59 -67
- package/src/FetchState.res.mjs +34 -44
- package/src/HandlerRegister.res +6 -4
- package/src/HandlerRegister.res.mjs +8 -5
- package/src/InMemoryStore.res +22 -32
- package/src/InMemoryStore.res.mjs +12 -24
- package/src/IndexerState.res +40 -3
- package/src/IndexerState.res.mjs +30 -4
- package/src/IndexerState.resi +4 -1
- package/src/Internal.res +16 -17
- package/src/Main.res +25 -15
- package/src/Main.res.mjs +19 -14
- package/src/Metrics.res +15 -1
- package/src/Metrics.res.mjs +5 -1
- package/src/Persistence.res +42 -19
- package/src/Persistence.res.mjs +8 -5
- package/src/PgStorage.res +232 -78
- package/src/PgStorage.res.mjs +114 -72
- package/src/PruneStaleHistory.res +1 -1
- package/src/PruneStaleHistory.res.mjs +1 -1
- package/src/Rollback.res +62 -41
- package/src/Rollback.res.mjs +61 -19
- package/src/RollbackCommit.res +7 -9
- package/src/RollbackCommit.res.mjs +5 -6
- package/src/SimulateItems.res +5 -12
- package/src/SimulateItems.res.mjs +6 -10
- package/src/Sink.res +35 -26
- package/src/Sink.res.mjs +20 -16
- package/src/TestIndexer.res +95 -113
- package/src/TestIndexer.res.mjs +63 -90
- package/src/Utils.res +38 -0
- package/src/Utils.res.mjs +39 -2
- package/src/Writing.res +4 -2
- package/src/Writing.res.mjs +3 -2
- package/src/bindings/ClickHouse.res +311 -683
- package/src/bindings/ClickHouse.res.mjs +294 -416
- package/src/bindings/ClickHouseSink.res +303 -0
- package/src/bindings/ClickHouseSink.res.mjs +216 -0
- package/src/bindings/NodeJs.res +9 -0
- package/src/bindings/NodeJs.res.mjs +8 -1
- package/src/bindings/Postgres.res +9 -0
- package/src/bindings/Postgres.res.mjs +3 -0
- package/src/db/EntityHistory.res +29 -24
- package/src/db/EntityHistory.res.mjs +11 -19
- package/src/db/InternalTable.res +247 -83
- package/src/db/InternalTable.res.mjs +216 -85
- package/src/db/RollbackScope.res +29 -0
- package/src/db/RollbackScope.res.mjs +31 -0
- package/src/db/Table.res +28 -1
- package/src/db/Table.res.mjs +26 -2
- package/src/resolvers/ResolverProcess.res +809 -0
- package/src/resolvers/ResolverProcess.res.mjs +667 -0
- package/src/resolvers/ResolverQuery.res +317 -0
- package/src/resolvers/ResolverQuery.res.mjs +220 -0
- package/src/resolvers/collisions.js +108 -0
- package/src/resolvers/db.js +363 -0
- package/src/resolvers/dispatch.js +165 -0
- package/src/resolvers/errors.js +30 -0
- package/src/resolvers/graphqlSelection.js +292 -0
- package/src/resolvers/hasuraAction.js +84 -0
- package/src/resolvers/hasuraApply.js +323 -0
- package/src/resolvers/hasuraMetadata.js +119 -0
- package/src/resolvers/index.js +95 -0
- package/src/resolvers/manifest.js +370 -0
- package/src/resolvers/server.js +270 -0
- package/src/sources/AddressSet.res +0 -22
- package/src/sources/AddressStore.res +48 -88
- package/src/sources/AddressStore.res.mjs +11 -22
- package/src/sources/Evm.res +2 -0
- package/src/sources/Evm.res.mjs +2 -0
- package/src/sources/Fuel.res +2 -0
- package/src/sources/Fuel.res.mjs +2 -0
- package/src/sources/Svm.res +2 -0
- package/src/sources/Svm.res.mjs +2 -0
- package/src/sources/SvmHyperSyncClient.res +13 -107
- package/src/sources/SvmHyperSyncClient.res.mjs +2 -5
- package/src/sources/SvmHyperSyncSource.res +5 -10
- package/src/sources/SvmHyperSyncSource.res.mjs +5 -13
- package/src/tui/Tui.res +1 -1
- package/src/tui/Tui.res.mjs +1 -1
- package/svm.schema.json +17 -78
- package/src/MemoryStorage.res +0 -756
- package/src/MemoryStorage.res.mjs +0 -623
package/src/Config.res
CHANGED
|
@@ -94,6 +94,9 @@ type t = {
|
|
|
94
94
|
name: string,
|
|
95
95
|
description: option<string>,
|
|
96
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>,
|
|
97
100
|
contractHandlers: array<contractHandler>,
|
|
98
101
|
shouldRollbackOnReorg: bool,
|
|
99
102
|
shouldSaveFullHistory: bool,
|
|
@@ -107,6 +110,9 @@ type t = {
|
|
|
107
110
|
// they can express fits an INTEGER.
|
|
108
111
|
chainIdMode: ChainId.mode,
|
|
109
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,
|
|
110
116
|
defaultChain: option<chain>,
|
|
111
117
|
ecosystem: Ecosystem.t,
|
|
112
118
|
enableRawEvents: bool,
|
|
@@ -123,76 +129,9 @@ type t = {
|
|
|
123
129
|
isDev: bool,
|
|
124
130
|
userEntitiesByName: dict<Internal.entityConfig>,
|
|
125
131
|
userEntities: array<Internal.entityConfig>,
|
|
126
|
-
allEntities: array<Internal.entityConfig>,
|
|
127
132
|
allEnums: array<Table.enumConfig<Table.enum>>,
|
|
128
133
|
}
|
|
129
134
|
|
|
130
|
-
module EnvioAddresses = {
|
|
131
|
-
let name = "envio_addresses"
|
|
132
|
-
let index = -1
|
|
133
|
-
|
|
134
|
-
let makeId = (~chainId: ChainId.t, ~address) => {
|
|
135
|
-
chainId->ChainId.toString ++ "-" ++ address->Address.toString
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
type t = {
|
|
139
|
-
id: string,
|
|
140
|
-
@as("chain_id") chainId: ChainId.t,
|
|
141
|
-
@as("registration_block") registrationBlock: int,
|
|
142
|
-
// Vestigial: always written as -1 and never read back. The column stays so
|
|
143
|
-
// an existing schema doesn't need a migration.
|
|
144
|
-
@as("registration_log_index") registrationLogIndex: int,
|
|
145
|
-
@as("contract_name") contractName: string,
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Extract the raw contract address from the composite id ({chainId}-{address}).
|
|
149
|
-
// Inverse of makeId. Keep in sync with makeId above and the SUBSTRING SQL in
|
|
150
|
-
// InternalTable.Chains.makeGetInitialStateQuery.
|
|
151
|
-
let getAddress = (entity: t): Address.t => {
|
|
152
|
-
let sepIdx = entity.id->String.indexOf("-")
|
|
153
|
-
entity.id
|
|
154
|
-
->String.slice(~start=sepIdx + 1, ~end=entity.id->String.length)
|
|
155
|
-
->Address.unsafeFromString
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
let schema = S.schema(s => {
|
|
159
|
-
id: s.matches(S.string),
|
|
160
|
-
chainId: s.matches(ChainId.schema),
|
|
161
|
-
registrationBlock: s.matches(S.int),
|
|
162
|
-
registrationLogIndex: s.matches(S.int),
|
|
163
|
-
contractName: s.matches(S.string),
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
let table = Table.mkTable(
|
|
167
|
-
name,
|
|
168
|
-
~fields=[
|
|
169
|
-
Table.mkField("id", String, ~isPrimaryKey=true, ~fieldSchema=S.string),
|
|
170
|
-
Table.mkField("chain_id", ChainId, ~fieldSchema=ChainId.schema),
|
|
171
|
-
Table.mkField("registration_block", Int32, ~fieldSchema=S.int),
|
|
172
|
-
// -1 sentinel when registered from a block handler (no log index)
|
|
173
|
-
Table.mkField("registration_log_index", Int32, ~fieldSchema=S.int),
|
|
174
|
-
Table.mkField("contract_name", String, ~fieldSchema=S.string),
|
|
175
|
-
],
|
|
176
|
-
)
|
|
177
|
-
|
|
178
|
-
external castToInternal: t => Internal.entity = "%identity"
|
|
179
|
-
|
|
180
|
-
let entityConfig = {
|
|
181
|
-
Internal.name,
|
|
182
|
-
index,
|
|
183
|
-
schema,
|
|
184
|
-
table,
|
|
185
|
-
// Internal address tracking is Postgres-only; the global config is
|
|
186
|
-
// always required to have Postgres enabled (Storage::resolve forbids
|
|
187
|
-
// a Postgres-disabled global), so this is safe regardless of mode.
|
|
188
|
-
storage: {postgres: true, clickhouse: false},
|
|
189
|
-
// The table already keys rows by chain through the composite `id`, so the
|
|
190
|
-
// per-chain mode must not append a second chain-id column to it.
|
|
191
|
-
crossChain: true,
|
|
192
|
-
internal: true,
|
|
193
|
-
}->Internal.fromGenericEntityConfig
|
|
194
|
-
}
|
|
195
|
-
|
|
196
135
|
type rpcSourceFor = | @as("sync") Sync | @as("fallback") Fallback | @as("realtime") Realtime
|
|
197
136
|
|
|
198
137
|
let rpcSourceForSchema = S.enum([Sync, Fallback, Realtime])
|
|
@@ -241,24 +180,6 @@ let publicConfigChainSchema = S.schema(s =>
|
|
|
241
180
|
let svmEventDescriptorSchema = S.schema(s =>
|
|
242
181
|
{
|
|
243
182
|
"discriminator": s.matches(S.option(S.string)),
|
|
244
|
-
"discriminatorByteLen": s.matches(S.int),
|
|
245
|
-
// An array of AND-groups OR-ed together: the CLI normalizes both the flat
|
|
246
|
-
// and `any_of` YAML shapes to `Vec<Vec<SvmAccountFilterJson>>`.
|
|
247
|
-
"accountFilters": s.matches(
|
|
248
|
-
S.option(
|
|
249
|
-
S.array(
|
|
250
|
-
S.array(
|
|
251
|
-
S.schema(s =>
|
|
252
|
-
{
|
|
253
|
-
"position": s.matches(S.int),
|
|
254
|
-
"values": s.matches(S.array(S.string)),
|
|
255
|
-
}
|
|
256
|
-
),
|
|
257
|
-
),
|
|
258
|
-
),
|
|
259
|
-
),
|
|
260
|
-
),
|
|
261
|
-
"isInner": s.matches(S.option(S.bool)),
|
|
262
183
|
"accounts": s.matches(S.option(S.array(S.string))),
|
|
263
184
|
"args": s.matches(S.option(S.json(~validate=false))),
|
|
264
185
|
}
|
|
@@ -444,7 +365,7 @@ let getFieldTypeAndSchema = (prop, ~enumConfigsByName: dict<Table.enumConfig<Tab
|
|
|
444
365
|
baseSchema
|
|
445
366
|
}
|
|
446
367
|
let fieldSchema = if isNullable {
|
|
447
|
-
|
|
368
|
+
Utils.Schema.nullTolerant(fieldSchema)->S.toUnknown
|
|
448
369
|
} else {
|
|
449
370
|
fieldSchema
|
|
450
371
|
}
|
|
@@ -614,6 +535,7 @@ let publicConfigSchema = S.schema(s =>
|
|
|
614
535
|
"name": s.matches(S.string),
|
|
615
536
|
"description": s.matches(S.option(S.string)),
|
|
616
537
|
"handlers": s.matches(S.option(S.string)),
|
|
538
|
+
"resolvers": s.matches(S.option(S.string)),
|
|
617
539
|
"isDev": s.matches(S.option(S.bool)),
|
|
618
540
|
"fullBatchSize": s.matches(S.option(S.int)),
|
|
619
541
|
"rollbackOnReorg": s.matches(S.option(S.bool)),
|
|
@@ -630,6 +552,14 @@ let publicConfigSchema = S.schema(s =>
|
|
|
630
552
|
}
|
|
631
553
|
)
|
|
632
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
|
+
|
|
633
563
|
let fromPublic = (publicConfigJson: JSON.t) => {
|
|
634
564
|
let maxAddrInPartition = Env.maxAddrInPartition
|
|
635
565
|
// Parse public config
|
|
@@ -809,11 +739,6 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
809
739
|
Utils.magic: _ => {
|
|
810
740
|
"svm": option<{
|
|
811
741
|
"discriminator": option<string>,
|
|
812
|
-
"discriminatorByteLen": int,
|
|
813
|
-
"accountFilters": option<
|
|
814
|
-
array<array<{"position": int, "values": array<string>}>>,
|
|
815
|
-
>,
|
|
816
|
-
"isInner": option<bool>,
|
|
817
742
|
"accounts": option<array<string>>,
|
|
818
743
|
"args": option<JSON.t>,
|
|
819
744
|
}>,
|
|
@@ -826,25 +751,11 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
826
751
|
`SVM instruction ${contractName}.${eventName} is missing the "svm" descriptor in internal config`,
|
|
827
752
|
)
|
|
828
753
|
}
|
|
829
|
-
let accountFilters =
|
|
830
|
-
svm["accountFilters"]
|
|
831
|
-
->Option.getOr([])
|
|
832
|
-
->Array.map(group =>
|
|
833
|
-
group->Array.map(
|
|
834
|
-
af => {
|
|
835
|
-
Internal.position: af["position"],
|
|
836
|
-
values: af["values"]->SvmTypes.Pubkey.fromStringsUnsafe,
|
|
837
|
-
},
|
|
838
|
-
)
|
|
839
|
-
)
|
|
840
754
|
(EventConfigBuilder.buildSvmInstructionEventConfig(
|
|
841
755
|
~contractName,
|
|
842
756
|
~instructionName=eventName,
|
|
843
757
|
~programId,
|
|
844
758
|
~discriminator=svm["discriminator"],
|
|
845
|
-
~discriminatorByteLen=svm["discriminatorByteLen"],
|
|
846
|
-
~accountFilters,
|
|
847
|
-
~isInner=svm["isInner"],
|
|
848
759
|
~accounts=svm["accounts"]->Option.getOr([]),
|
|
849
760
|
~args=svm["args"]->Option.getOr(JSON.Null),
|
|
850
761
|
~definedTypes=svmDefinedTypes,
|
|
@@ -931,25 +842,24 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
931
842
|
}
|
|
932
843
|
})
|
|
933
844
|
|
|
934
|
-
//
|
|
935
|
-
//
|
|
936
|
-
//
|
|
937
|
-
//
|
|
938
|
-
// (checksum or lowercase), so an exact match
|
|
939
|
-
|
|
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()
|
|
940
852
|
contracts->Array.forEach(contract => {
|
|
941
853
|
contract.addresses->Array.forEach(
|
|
942
854
|
address => {
|
|
943
855
|
let addressString = address->Address.toString
|
|
944
|
-
|
|
945
|
-
|
|
856
|
+
let key = contract.name ++ "-" ++ addressString
|
|
857
|
+
if seen->Utils.Set.has(key) {
|
|
946
858
|
JsError.throwWithMessage(
|
|
947
|
-
|
|
948
|
-
? `Address ${addressString} is listed multiple times for the contract ${contract.name} on chain ${chainId->ChainId.toString}. Please remove the duplicate from your config.`
|
|
949
|
-
: `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.`,
|
|
950
860
|
)
|
|
951
|
-
| None => contractNameByAddress->Dict.set(addressString, contract.name)
|
|
952
861
|
}
|
|
862
|
+
seen->Utils.Set.add(key)->ignore
|
|
953
863
|
},
|
|
954
864
|
)
|
|
955
865
|
})
|
|
@@ -1075,8 +985,6 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
1075
985
|
->Option.getOr([])
|
|
1076
986
|
->parseEntitiesFromJson(~enumConfigsByName, ~globalStorage, ~defaultCrossChain)
|
|
1077
987
|
|
|
1078
|
-
let allEntities = userEntities->Array.concat([EnvioAddresses.entityConfig])
|
|
1079
|
-
|
|
1080
988
|
// Keyed by the capitalized entity name to match the handler-context
|
|
1081
989
|
// accessor (`context.Pool_snapshots`) the generated types expose, while
|
|
1082
990
|
// entityConfig.name stays the original schema name used for the physical
|
|
@@ -1106,6 +1014,7 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
1106
1014
|
name: publicConfig["name"],
|
|
1107
1015
|
description: publicConfig["description"],
|
|
1108
1016
|
handlers: publicConfig["handlers"]->Option.getOr("src/handlers"),
|
|
1017
|
+
resolvers: publicConfig["resolvers"],
|
|
1109
1018
|
contractHandlers,
|
|
1110
1019
|
shouldRollbackOnReorg: publicConfig["rollbackOnReorg"]->Option.getOr(true),
|
|
1111
1020
|
shouldSaveFullHistory: publicConfig["saveFullHistory"]->Option.getOr(false),
|
|
@@ -1113,6 +1022,7 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
1113
1022
|
storage: globalStorage,
|
|
1114
1023
|
chainIdMode: publicConfig["chainIdMode"]->Option.getOr(Int32),
|
|
1115
1024
|
chainMap,
|
|
1025
|
+
contractMapping: contractMappingOf(~chainConfigs=chains),
|
|
1116
1026
|
defaultChain: chains->Array.get(0),
|
|
1117
1027
|
enableRawEvents: publicConfig["rawEvents"]->Option.getOr(false),
|
|
1118
1028
|
ecosystem,
|
|
@@ -1124,11 +1034,18 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
1124
1034
|
isDev: publicConfig["isDev"]->Option.getOr(false),
|
|
1125
1035
|
userEntitiesByName,
|
|
1126
1036
|
userEntities,
|
|
1127
|
-
allEntities,
|
|
1128
1037
|
allEnums,
|
|
1129
1038
|
}
|
|
1130
1039
|
}
|
|
1131
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
|
+
|
|
1132
1049
|
// Canonicalize a user-provided address to the configured casing so it matches
|
|
1133
1050
|
// addresses parsed from config.yaml during routing. HyperSync/RPC data arrives
|
|
1134
1051
|
// already canonical; only user-land input (simulate srcAddress, contractRegister
|
|
@@ -1231,8 +1148,11 @@ let getPublicConfigJson = () =>
|
|
|
1231
1148
|
|
|
1232
1149
|
// Drops source URLs from each chain so RPC/hypersync edits don't trigger
|
|
1233
1150
|
// the resume-time compat check (and don't end up in `envio_info`). Also
|
|
1234
|
-
// drops
|
|
1235
|
-
//
|
|
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.
|
|
1236
1156
|
let stripSensitiveData = (json: JSON.t): JSON.t => {
|
|
1237
1157
|
let cloned = json->JSON.stringify->JSON.parseOrThrow
|
|
1238
1158
|
let stripChains = (ecosystem: option<JSON.t>) =>
|
|
@@ -1259,6 +1179,7 @@ let stripSensitiveData = (json: JSON.t): JSON.t => {
|
|
|
1259
1179
|
switch cloned {
|
|
1260
1180
|
| Object(obj) => {
|
|
1261
1181
|
obj->Utils.Dict.deleteInPlace("isDev")
|
|
1182
|
+
obj->Utils.Dict.deleteInPlace("resolvers")
|
|
1262
1183
|
stripChains(obj->Dict.get("evm"))
|
|
1263
1184
|
stripChains(obj->Dict.get("fuel"))
|
|
1264
1185
|
stripChains(obj->Dict.get("svm"))
|
package/src/Config.res.mjs
CHANGED
|
@@ -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,58 +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
|
-
internal: true
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
let EnvioAddresses = {
|
|
78
|
-
name: name,
|
|
79
|
-
index: -1,
|
|
80
|
-
makeId: makeId,
|
|
81
|
-
getAddress: getAddress,
|
|
82
|
-
schema: schema,
|
|
83
|
-
table: table,
|
|
84
|
-
entityConfig: entityConfig
|
|
85
|
-
};
|
|
86
|
-
|
|
87
36
|
let rpcSourceForSchema = S$RescriptSchema.$$enum([
|
|
88
37
|
"sync",
|
|
89
38
|
"fallback",
|
|
@@ -124,12 +73,6 @@ let publicConfigChainSchema = S$RescriptSchema.schema(s => ({
|
|
|
124
73
|
|
|
125
74
|
let svmEventDescriptorSchema = S$RescriptSchema.schema(s => ({
|
|
126
75
|
discriminator: s.m(S$RescriptSchema.option(S$RescriptSchema.string)),
|
|
127
|
-
discriminatorByteLen: s.m(S$RescriptSchema.int),
|
|
128
|
-
accountFilters: s.m(S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.array(S$RescriptSchema.schema(s => ({
|
|
129
|
-
position: s.m(S$RescriptSchema.int),
|
|
130
|
-
values: s.m(S$RescriptSchema.array(S$RescriptSchema.string))
|
|
131
|
-
})))))),
|
|
132
|
-
isInner: s.m(S$RescriptSchema.option(S$RescriptSchema.bool)),
|
|
133
76
|
accounts: s.m(S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.string))),
|
|
134
77
|
args: s.m(S$RescriptSchema.option(S$RescriptSchema.json(false)))
|
|
135
78
|
}));
|
|
@@ -329,7 +272,7 @@ function getFieldTypeAndSchema(prop, enumConfigsByName) {
|
|
|
329
272
|
}
|
|
330
273
|
let baseSchema = match[1];
|
|
331
274
|
let fieldSchema = isArray ? S$RescriptSchema.array(baseSchema) : baseSchema;
|
|
332
|
-
let fieldSchema$1 = isNullable ?
|
|
275
|
+
let fieldSchema$1 = isNullable ? Utils.Schema.nullTolerant(fieldSchema) : fieldSchema;
|
|
333
276
|
return [
|
|
334
277
|
match[0],
|
|
335
278
|
fieldSchema$1,
|
|
@@ -429,6 +372,7 @@ let publicConfigSchema = S$RescriptSchema.schema(s => ({
|
|
|
429
372
|
name: s.m(S$RescriptSchema.string),
|
|
430
373
|
description: s.m(S$RescriptSchema.option(S$RescriptSchema.string)),
|
|
431
374
|
handlers: s.m(S$RescriptSchema.option(S$RescriptSchema.string)),
|
|
375
|
+
resolvers: s.m(S$RescriptSchema.option(S$RescriptSchema.string)),
|
|
432
376
|
isDev: s.m(S$RescriptSchema.option(S$RescriptSchema.bool)),
|
|
433
377
|
fullBatchSize: s.m(S$RescriptSchema.option(S$RescriptSchema.int)),
|
|
434
378
|
rollbackOnReorg: s.m(S$RescriptSchema.option(S$RescriptSchema.bool)),
|
|
@@ -444,6 +388,16 @@ let publicConfigSchema = S$RescriptSchema.schema(s => ({
|
|
|
444
388
|
entities: s.m(S$RescriptSchema.option(S$RescriptSchema.array(entityJsonSchema)))
|
|
445
389
|
}));
|
|
446
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
|
+
|
|
447
401
|
function fromPublic(publicConfigJson) {
|
|
448
402
|
let publicConfig;
|
|
449
403
|
try {
|
|
@@ -568,11 +522,7 @@ function fromPublic(publicConfigJson) {
|
|
|
568
522
|
) : addresses[0];
|
|
569
523
|
let s = eventItem.svm;
|
|
570
524
|
let svm = s !== undefined ? Primitive_option.valFromOption(s) : Stdlib_JsError.throwWithMessage(`SVM instruction ` + contractName + `.` + eventName + ` is missing the "svm" descriptor in internal config`);
|
|
571
|
-
|
|
572
|
-
position: af.position,
|
|
573
|
-
values: af.values
|
|
574
|
-
})));
|
|
575
|
-
return EventConfigBuilder.buildSvmInstructionEventConfig(contractName, eventName, programId, svm.discriminator, svm.discriminatorByteLen, 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);
|
|
576
526
|
}
|
|
577
527
|
});
|
|
578
528
|
} else {
|
|
@@ -622,16 +572,14 @@ function fromPublic(publicConfigJson) {
|
|
|
622
572
|
startBlock: startBlock
|
|
623
573
|
};
|
|
624
574
|
});
|
|
625
|
-
let
|
|
575
|
+
let seen = new Set();
|
|
626
576
|
contracts.forEach(contract => {
|
|
627
577
|
contract.addresses.forEach(address => {
|
|
628
|
-
let
|
|
629
|
-
if (
|
|
630
|
-
|
|
631
|
-
} else {
|
|
632
|
-
contractNameByAddress[address] = contract.name;
|
|
633
|
-
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.`);
|
|
634
581
|
}
|
|
582
|
+
seen.add(key);
|
|
635
583
|
});
|
|
636
584
|
});
|
|
637
585
|
let sourceConfig;
|
|
@@ -734,7 +682,6 @@ function fromPublic(publicConfigJson) {
|
|
|
734
682
|
};
|
|
735
683
|
let defaultCrossChain = Stdlib_Option.getOr(publicConfig.defaultCrossChain, true);
|
|
736
684
|
let userEntities = parseEntitiesFromJson(Stdlib_Option.getOr(publicConfig.entities, []), enumConfigsByName, globalStorage, defaultCrossChain);
|
|
737
|
-
let allEntities = userEntities.concat([entityConfig]);
|
|
738
685
|
let userEntitiesByName = Object.fromEntries(userEntities.map(entityConfig => [
|
|
739
686
|
Utils.$$String.capitalize(entityConfig.name),
|
|
740
687
|
entityConfig
|
|
@@ -747,6 +694,7 @@ function fromPublic(publicConfigJson) {
|
|
|
747
694
|
name: publicConfig.name,
|
|
748
695
|
description: publicConfig.description,
|
|
749
696
|
handlers: Stdlib_Option.getOr(publicConfig.handlers, "src/handlers"),
|
|
697
|
+
resolvers: publicConfig.resolvers,
|
|
750
698
|
contractHandlers: contractHandlers,
|
|
751
699
|
shouldRollbackOnReorg: Stdlib_Option.getOr(publicConfig.rollbackOnReorg, true),
|
|
752
700
|
shouldSaveFullHistory: Stdlib_Option.getOr(publicConfig.saveFullHistory, false),
|
|
@@ -754,6 +702,7 @@ function fromPublic(publicConfigJson) {
|
|
|
754
702
|
storage: globalStorage,
|
|
755
703
|
chainIdMode: Stdlib_Option.getOr(publicConfig.chainIdMode, "int32"),
|
|
756
704
|
chainMap: chainMap,
|
|
705
|
+
contractMapping: contractMappingOf(chains),
|
|
757
706
|
defaultChain: chains[0],
|
|
758
707
|
ecosystem: ecosystem,
|
|
759
708
|
enableRawEvents: Stdlib_Option.getOr(publicConfig.rawEvents, false),
|
|
@@ -765,11 +714,18 @@ function fromPublic(publicConfigJson) {
|
|
|
765
714
|
isDev: Stdlib_Option.getOr(publicConfig.isDev, false),
|
|
766
715
|
userEntitiesByName: userEntitiesByName,
|
|
767
716
|
userEntities: userEntities,
|
|
768
|
-
allEntities: allEntities,
|
|
769
717
|
allEnums: allEnums
|
|
770
718
|
};
|
|
771
719
|
}
|
|
772
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
|
+
|
|
773
729
|
function normalizeUserAddress(config, address) {
|
|
774
730
|
let match = config.ecosystem.name;
|
|
775
731
|
switch (match) {
|
|
@@ -908,6 +864,7 @@ function stripSensitiveData(json) {
|
|
|
908
864
|
};
|
|
909
865
|
if (typeof cloned === "object" && cloned !== null && !Array.isArray(cloned)) {
|
|
910
866
|
Utils.Dict.deleteInPlace(cloned, "isDev");
|
|
867
|
+
Utils.Dict.deleteInPlace(cloned, "resolvers");
|
|
911
868
|
stripChains(cloned["evm"]);
|
|
912
869
|
stripChains(cloned["fuel"]);
|
|
913
870
|
stripChains(cloned["svm"]);
|
|
@@ -1092,7 +1049,6 @@ function getPgUserEntities(config) {
|
|
|
1092
1049
|
export {
|
|
1093
1050
|
chainIdFieldName,
|
|
1094
1051
|
chainIdColumnName,
|
|
1095
|
-
EnvioAddresses,
|
|
1096
1052
|
rpcSourceForSchema,
|
|
1097
1053
|
rpcConfigSchema,
|
|
1098
1054
|
chainContractSchema,
|
|
@@ -1118,7 +1074,9 @@ export {
|
|
|
1118
1074
|
columnNameFormatSchema,
|
|
1119
1075
|
publicConfigStorageSchema,
|
|
1120
1076
|
publicConfigSchema,
|
|
1077
|
+
contractMappingOf,
|
|
1121
1078
|
fromPublic,
|
|
1079
|
+
isIsolatedMultichain,
|
|
1122
1080
|
normalizeUserAddress,
|
|
1123
1081
|
normalizeSimulateAddress,
|
|
1124
1082
|
getEventConfig,
|
|
@@ -1134,4 +1092,4 @@ export {
|
|
|
1134
1092
|
load,
|
|
1135
1093
|
getPgUserEntities,
|
|
1136
1094
|
}
|
|
1137
|
-
/*
|
|
1095
|
+
/* rpcSourceForSchema Not a pure module */
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// The contract name <-> id mapping every stored address row references: a
|
|
2
|
+
// name's position in `names` is the id `envio_contracts` holds and
|
|
3
|
+
// `envio_addresses.contract_id` points at. Ids must mean the same thing on
|
|
4
|
+
// every chain and across restarts, so a mapping is built once — from the whole
|
|
5
|
+
// config, never from a filtered subset — and read everywhere else.
|
|
6
|
+
type t = {
|
|
7
|
+
// Id order. Index i is the name of contract id i.
|
|
8
|
+
names: array<string>,
|
|
9
|
+
idByName: dict<int>,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let indexNames = (names: array<string>): t => {
|
|
13
|
+
let idByName = Dict.make()
|
|
14
|
+
names->Array.forEachWithIndex((name, id) => idByName->Dict.set(name, id))
|
|
15
|
+
{names, idByName}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Ids are 0-based positions in a smallint column, so 32768 contracts fit.
|
|
19
|
+
let maxContracts = 32768
|
|
20
|
+
|
|
21
|
+
// Names in any order; the codec puts them in byte order so the ids never depend
|
|
22
|
+
// on the order contracts happen to be declared in.
|
|
23
|
+
let make = (~names: array<string>): t => {
|
|
24
|
+
let canonical = Core.getAddon().canonicalContractNames(names)
|
|
25
|
+
if canonical->Array.length > maxContracts {
|
|
26
|
+
JsError.throwWithMessage(
|
|
27
|
+
`The indexer declares ${canonical
|
|
28
|
+
->Array.length
|
|
29
|
+
->Int.toString} contracts, more than the ${maxContracts->Int.toString} a smallint contract id can hold.`,
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
indexNames(canonical)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// What a storage holds before it has been initialized: no contract has an id
|
|
36
|
+
// yet, so every lookup fails rather than resolving against a stale mapping.
|
|
37
|
+
let empty = indexNames([])
|
|
38
|
+
|
|
39
|
+
// A mapping read back from storage. Taken verbatim: the stored order *is* the
|
|
40
|
+
// id order the stored rows were written against, so re-canonicalizing it would
|
|
41
|
+
// paper over a mapping that no longer matches those rows.
|
|
42
|
+
let fromStoredNames = indexNames
|
|
43
|
+
|
|
44
|
+
let names = (mapping: t) => mapping.names
|
|
45
|
+
|
|
46
|
+
let idOfOrThrow = (mapping: t, name, ~context="") =>
|
|
47
|
+
switch mapping.idByName->Utils.Dict.dangerouslyGetNonOption(name) {
|
|
48
|
+
| Some(id) => id
|
|
49
|
+
| None =>
|
|
50
|
+
JsError.throwWithMessage(
|
|
51
|
+
`Contract "${name}"${context} is missing from the indexer's contract list.`,
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let nameOfOrThrow = (mapping: t, id) =>
|
|
56
|
+
switch mapping.names->Array.get(id) {
|
|
57
|
+
| Some(name) => name
|
|
58
|
+
| None =>
|
|
59
|
+
JsError.throwWithMessage(
|
|
60
|
+
`Contract id ${id->Int.toString} is outside the indexer's contract list.`,
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let isEqual = (a: t, b: t) =>
|
|
65
|
+
a.names->Array.length === b.names->Array.length &&
|
|
66
|
+
a.names->Array.everyWithIndex((name, idx) => b.names->Array.getUnsafe(idx) === name)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Core from "./Core.res.mjs";
|
|
4
|
+
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
5
|
+
|
|
6
|
+
function indexNames(names) {
|
|
7
|
+
let idByName = {};
|
|
8
|
+
names.forEach((name, id) => {
|
|
9
|
+
idByName[name] = id;
|
|
10
|
+
});
|
|
11
|
+
return {
|
|
12
|
+
names: names,
|
|
13
|
+
idByName: idByName
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function make(names) {
|
|
18
|
+
let canonical = Core.getAddon().canonicalContractNames(names);
|
|
19
|
+
if (canonical.length > 32768) {
|
|
20
|
+
Stdlib_JsError.throwWithMessage(`The indexer declares ` + canonical.length.toString() + ` contracts, more than the ` + (32768).toString() + ` a smallint contract id can hold.`);
|
|
21
|
+
}
|
|
22
|
+
return indexNames(canonical);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let empty = indexNames([]);
|
|
26
|
+
|
|
27
|
+
function names(mapping) {
|
|
28
|
+
return mapping.names;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function idOfOrThrow(mapping, name, contextOpt) {
|
|
32
|
+
let context = contextOpt !== undefined ? contextOpt : "";
|
|
33
|
+
let id = mapping.idByName[name];
|
|
34
|
+
if (id !== undefined) {
|
|
35
|
+
return id;
|
|
36
|
+
} else {
|
|
37
|
+
return Stdlib_JsError.throwWithMessage(`Contract "` + name + `"` + context + ` is missing from the indexer's contract list.`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function nameOfOrThrow(mapping, id) {
|
|
42
|
+
let name = mapping.names[id];
|
|
43
|
+
if (name !== undefined) {
|
|
44
|
+
return name;
|
|
45
|
+
} else {
|
|
46
|
+
return Stdlib_JsError.throwWithMessage(`Contract id ` + id.toString() + ` is outside the indexer's contract list.`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isEqual(a, b) {
|
|
51
|
+
if (a.names.length === b.names.length) {
|
|
52
|
+
return a.names.every((name, idx) => b.names[idx] === name);
|
|
53
|
+
} else {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let maxContracts = 32768;
|
|
59
|
+
|
|
60
|
+
let fromStoredNames = indexNames;
|
|
61
|
+
|
|
62
|
+
export {
|
|
63
|
+
indexNames,
|
|
64
|
+
maxContracts,
|
|
65
|
+
make,
|
|
66
|
+
empty,
|
|
67
|
+
fromStoredNames,
|
|
68
|
+
names,
|
|
69
|
+
idOfOrThrow,
|
|
70
|
+
nameOfOrThrow,
|
|
71
|
+
isEqual,
|
|
72
|
+
}
|
|
73
|
+
/* empty Not a pure module */
|