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