envio 3.0.0-alpha.21 → 3.0.0-alpha.23
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 +3 -3
- package/bin.mjs +2 -48
- package/evm.schema.json +67 -0
- package/fuel.schema.json +67 -0
- package/index.d.ts +822 -38
- package/index.js +5 -3
- package/package.json +10 -8
- package/rescript.json +5 -9
- package/src/Address.res +4 -5
- package/src/Address.res.mjs +9 -12
- package/src/Api.res +15 -0
- package/src/Api.res.mjs +20 -0
- package/src/Batch.res +32 -34
- package/src/Batch.res.mjs +172 -187
- package/src/Bin.res +89 -0
- package/src/Bin.res.mjs +97 -0
- package/src/ChainFetcher.res +33 -57
- package/src/ChainFetcher.res.mjs +197 -227
- package/src/ChainManager.res +6 -14
- package/src/ChainManager.res.mjs +74 -85
- package/src/ChainMap.res +14 -16
- package/src/ChainMap.res.mjs +38 -38
- package/src/Config.res +193 -135
- package/src/Config.res.mjs +566 -592
- package/src/Core.res +182 -0
- package/src/Core.res.mjs +207 -0
- package/src/Ecosystem.res +25 -4
- package/src/Ecosystem.res.mjs +12 -13
- package/src/Env.res +20 -13
- package/src/Env.res.mjs +124 -113
- package/src/EnvSafe.res +269 -0
- package/src/EnvSafe.res.mjs +296 -0
- package/src/EnvSafe.resi +18 -0
- package/src/Envio.res +37 -26
- package/src/Envio.res.mjs +59 -60
- package/src/ErrorHandling.res +2 -2
- package/src/ErrorHandling.res.mjs +15 -15
- package/src/EventConfigBuilder.res +219 -81
- package/src/EventConfigBuilder.res.mjs +259 -202
- package/src/EventProcessing.res +27 -38
- package/src/EventProcessing.res.mjs +165 -183
- package/src/EventUtils.res +11 -11
- package/src/EventUtils.res.mjs +21 -22
- package/src/EvmTypes.res +0 -1
- package/src/EvmTypes.res.mjs +5 -5
- package/src/FetchState.res +360 -256
- package/src/FetchState.res.mjs +958 -914
- package/src/GlobalState.res +365 -351
- package/src/GlobalState.res.mjs +958 -992
- package/src/GlobalStateManager.res +1 -2
- package/src/GlobalStateManager.res.mjs +36 -44
- package/src/HandlerLoader.res +107 -23
- package/src/HandlerLoader.res.mjs +128 -38
- package/src/HandlerRegister.res +127 -103
- package/src/HandlerRegister.res.mjs +164 -164
- package/src/HandlerRegister.resi +12 -4
- package/src/Hasura.res +35 -22
- package/src/Hasura.res.mjs +158 -167
- package/src/InMemoryStore.res +20 -27
- package/src/InMemoryStore.res.mjs +64 -80
- package/src/InMemoryTable.res +34 -39
- package/src/InMemoryTable.res.mjs +165 -170
- package/src/Internal.res +52 -33
- package/src/Internal.res.mjs +84 -81
- package/src/LazyLoader.res.mjs +55 -61
- package/src/LoadLayer.res +77 -78
- package/src/LoadLayer.res.mjs +160 -189
- package/src/LoadManager.res +16 -21
- package/src/LoadManager.res.mjs +79 -84
- package/src/LogSelection.res +236 -68
- package/src/LogSelection.res.mjs +211 -141
- package/src/Logging.res +13 -9
- package/src/Logging.res.mjs +130 -143
- package/src/Main.res +430 -51
- package/src/Main.res.mjs +530 -271
- package/src/Persistence.res +80 -84
- package/src/Persistence.res.mjs +131 -132
- package/src/PgStorage.res +294 -167
- package/src/PgStorage.res.mjs +799 -817
- package/src/Prometheus.res +50 -58
- package/src/Prometheus.res.mjs +345 -373
- package/src/ReorgDetection.res +22 -24
- package/src/ReorgDetection.res.mjs +100 -106
- package/src/SafeCheckpointTracking.res +7 -7
- package/src/SafeCheckpointTracking.res.mjs +40 -43
- package/src/SimulateItems.res +41 -49
- package/src/SimulateItems.res.mjs +257 -272
- package/src/Sink.res +2 -2
- package/src/Sink.res.mjs +22 -26
- package/src/TableIndices.res +1 -2
- package/src/TableIndices.res.mjs +42 -48
- package/src/TestIndexer.res +196 -189
- package/src/TestIndexer.res.mjs +536 -536
- package/src/TestIndexerProxyStorage.res +16 -16
- package/src/TestIndexerProxyStorage.res.mjs +99 -122
- package/src/TestIndexerWorker.res +4 -0
- package/src/TestIndexerWorker.res.mjs +7 -0
- package/src/Throttler.res +3 -3
- package/src/Throttler.res.mjs +23 -24
- package/src/Time.res +1 -1
- package/src/Time.res.mjs +18 -21
- package/src/TopicFilter.res +3 -3
- package/src/TopicFilter.res.mjs +29 -30
- package/src/UserContext.res +93 -54
- package/src/UserContext.res.mjs +197 -182
- package/src/Utils.res +141 -86
- package/src/Utils.res.mjs +334 -295
- package/src/bindings/BigDecimal.res +0 -2
- package/src/bindings/BigDecimal.res.mjs +19 -23
- package/src/bindings/ClickHouse.res +28 -27
- package/src/bindings/ClickHouse.res.mjs +243 -240
- package/src/bindings/DateFns.res +11 -11
- package/src/bindings/DateFns.res.mjs +7 -7
- package/src/bindings/EventSource.res.mjs +2 -2
- package/src/bindings/Express.res +2 -5
- package/src/bindings/Hrtime.res +2 -2
- package/src/bindings/Hrtime.res.mjs +30 -32
- package/src/bindings/Lodash.res.mjs +1 -1
- package/src/bindings/NodeJs.res +14 -9
- package/src/bindings/NodeJs.res.mjs +20 -20
- package/src/bindings/Pino.res +8 -10
- package/src/bindings/Pino.res.mjs +40 -43
- package/src/bindings/Postgres.res +7 -5
- package/src/bindings/Postgres.res.mjs +9 -9
- package/src/bindings/PromClient.res +17 -2
- package/src/bindings/PromClient.res.mjs +30 -7
- package/src/bindings/SDSL.res.mjs +2 -2
- package/src/bindings/Viem.res +4 -4
- package/src/bindings/Viem.res.mjs +20 -22
- package/src/bindings/Vitest.res +1 -1
- package/src/bindings/Vitest.res.mjs +2 -2
- package/src/bindings/WebSocket.res +1 -1
- package/src/db/EntityHistory.res +9 -3
- package/src/db/EntityHistory.res.mjs +84 -59
- package/src/db/InternalTable.res +62 -60
- package/src/db/InternalTable.res.mjs +271 -203
- package/src/db/Schema.res +1 -2
- package/src/db/Schema.res.mjs +28 -32
- package/src/db/Table.res +28 -27
- package/src/db/Table.res.mjs +276 -292
- package/src/sources/EventRouter.res +21 -16
- package/src/sources/EventRouter.res.mjs +55 -57
- package/src/sources/Evm.res +17 -1
- package/src/sources/Evm.res.mjs +16 -8
- package/src/sources/EvmChain.res +15 -17
- package/src/sources/EvmChain.res.mjs +40 -42
- package/src/sources/Fuel.res +14 -1
- package/src/sources/Fuel.res.mjs +16 -8
- package/src/sources/FuelSDK.res +1 -1
- package/src/sources/FuelSDK.res.mjs +6 -8
- package/src/sources/HyperFuel.res +8 -10
- package/src/sources/HyperFuel.res.mjs +113 -123
- package/src/sources/HyperFuelClient.res.mjs +6 -7
- package/src/sources/HyperFuelSource.res +19 -20
- package/src/sources/HyperFuelSource.res.mjs +339 -356
- package/src/sources/HyperSync.res +11 -13
- package/src/sources/HyperSync.res.mjs +206 -220
- package/src/sources/HyperSyncClient.res +5 -7
- package/src/sources/HyperSyncClient.res.mjs +70 -75
- package/src/sources/HyperSyncHeightStream.res +8 -9
- package/src/sources/HyperSyncHeightStream.res.mjs +78 -86
- package/src/sources/HyperSyncJsonApi.res +18 -15
- package/src/sources/HyperSyncJsonApi.res.mjs +201 -231
- package/src/sources/HyperSyncSource.res +17 -21
- package/src/sources/HyperSyncSource.res.mjs +268 -290
- package/src/sources/Rpc.res +5 -5
- package/src/sources/Rpc.res.mjs +168 -192
- package/src/sources/RpcSource.res +166 -167
- package/src/sources/RpcSource.res.mjs +972 -1046
- package/src/sources/RpcWebSocketHeightStream.res +10 -11
- package/src/sources/RpcWebSocketHeightStream.res.mjs +131 -145
- package/src/sources/SimulateSource.res +1 -1
- package/src/sources/SimulateSource.res.mjs +35 -38
- package/src/sources/Source.res +1 -1
- package/src/sources/Source.res.mjs +3 -3
- package/src/sources/SourceManager.res +39 -20
- package/src/sources/SourceManager.res.mjs +340 -371
- package/src/sources/SourceManager.resi +2 -1
- package/src/sources/Svm.res +12 -5
- package/src/sources/Svm.res.mjs +44 -41
- package/src/tui/Tui.res +23 -12
- package/src/tui/Tui.res.mjs +292 -290
- package/src/tui/bindings/Ink.res +2 -4
- package/src/tui/bindings/Ink.res.mjs +35 -41
- package/src/tui/components/BufferedProgressBar.res +7 -7
- package/src/tui/components/BufferedProgressBar.res.mjs +46 -46
- package/src/tui/components/CustomHooks.res +1 -2
- package/src/tui/components/CustomHooks.res.mjs +102 -122
- package/src/tui/components/Messages.res +1 -2
- package/src/tui/components/Messages.res.mjs +38 -42
- package/src/tui/components/SyncETA.res +10 -11
- package/src/tui/components/SyncETA.res.mjs +178 -196
- package/src/tui/components/TuiData.res +1 -1
- package/src/tui/components/TuiData.res.mjs +7 -6
- package/src/vendored/Rest.res +52 -66
- package/src/vendored/Rest.res.mjs +324 -364
- package/svm.schema.json +67 -0
- package/src/Address.gen.ts +0 -8
- package/src/Config.gen.ts +0 -19
- package/src/Envio.gen.ts +0 -55
- package/src/EvmTypes.gen.ts +0 -6
- package/src/InMemoryStore.gen.ts +0 -6
- package/src/Internal.gen.ts +0 -64
- package/src/PgStorage.gen.ts +0 -10
- package/src/PgStorage.res.d.mts +0 -5
- package/src/Types.ts +0 -56
- package/src/bindings/BigDecimal.gen.ts +0 -14
- package/src/bindings/BigDecimal.res.d.mts +0 -5
- package/src/bindings/BigInt.gen.ts +0 -10
- package/src/bindings/BigInt.res +0 -70
- package/src/bindings/BigInt.res.d.mts +0 -5
- package/src/bindings/BigInt.res.mjs +0 -154
- package/src/bindings/Ethers.res.d.mts +0 -5
- package/src/bindings/Pino.gen.ts +0 -17
- package/src/bindings/Postgres.gen.ts +0 -8
- package/src/bindings/Postgres.res.d.mts +0 -5
- package/src/bindings/Promise.res +0 -67
- package/src/bindings/Promise.res.mjs +0 -26
- package/src/db/InternalTable.gen.ts +0 -36
- package/src/sources/HyperSyncClient.gen.ts +0 -19
package/src/Config.res
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
open Belt
|
|
2
|
-
|
|
3
1
|
type sourceSyncOptions = {
|
|
4
2
|
initialBlockInterval?: int,
|
|
5
3
|
backoffMultiplicative?: float,
|
|
@@ -21,7 +19,7 @@ type contract = {
|
|
|
21
19
|
eventSignatures: array<string>,
|
|
22
20
|
}
|
|
23
21
|
|
|
24
|
-
//
|
|
22
|
+
// Sources are instantiated lazily in ChainFetcher from this config.
|
|
25
23
|
type evmRpcConfig = {
|
|
26
24
|
url: string,
|
|
27
25
|
sourceFor: Source.sourceFor,
|
|
@@ -62,6 +60,11 @@ type multichain = Internal.multichain =
|
|
|
62
60
|
| @as("ordered") Ordered
|
|
63
61
|
| @as("unordered") Unordered
|
|
64
62
|
|
|
63
|
+
type storage = {
|
|
64
|
+
postgres: bool,
|
|
65
|
+
clickhouse: bool,
|
|
66
|
+
}
|
|
67
|
+
|
|
65
68
|
type contractHandler = {
|
|
66
69
|
name: string,
|
|
67
70
|
handler: option<string>,
|
|
@@ -74,6 +77,7 @@ type t = {
|
|
|
74
77
|
contractHandlers: array<contractHandler>,
|
|
75
78
|
shouldRollbackOnReorg: bool,
|
|
76
79
|
shouldSaveFullHistory: bool,
|
|
80
|
+
storage: storage,
|
|
77
81
|
multichain: multichain,
|
|
78
82
|
chainMap: ChainMap.t<chain>,
|
|
79
83
|
defaultChain: option<chain>,
|
|
@@ -82,45 +86,44 @@ type t = {
|
|
|
82
86
|
maxAddrInPartition: int,
|
|
83
87
|
batchSize: int,
|
|
84
88
|
lowercaseAddresses: bool,
|
|
85
|
-
addContractNameToContractNameMapping: dict<string>,
|
|
86
89
|
userEntitiesByName: dict<Internal.entityConfig>,
|
|
87
90
|
userEntities: array<Internal.entityConfig>,
|
|
88
91
|
allEntities: array<Internal.entityConfig>,
|
|
89
92
|
allEnums: array<Table.enumConfig<Table.enum>>,
|
|
90
93
|
}
|
|
91
94
|
|
|
92
|
-
module
|
|
93
|
-
let name = "
|
|
95
|
+
module EnvioAddresses = {
|
|
96
|
+
let name = "envio_addresses"
|
|
94
97
|
let index = -1
|
|
95
98
|
|
|
96
|
-
let makeId = (~chainId, ~
|
|
97
|
-
chainId->Belt.Int.toString ++ "-" ++
|
|
99
|
+
let makeId = (~chainId, ~address) => {
|
|
100
|
+
chainId->Belt.Int.toString ++ "-" ++ address->Address.toString
|
|
98
101
|
}
|
|
99
102
|
|
|
100
|
-
@genType
|
|
101
103
|
type t = {
|
|
102
104
|
id: string,
|
|
103
105
|
@as("chain_id") chainId: int,
|
|
104
|
-
@as("
|
|
105
|
-
|
|
106
|
-
@as("
|
|
107
|
-
@as("registering_event_contract_name") registeringEventContractName: string,
|
|
108
|
-
@as("registering_event_name") registeringEventName: string,
|
|
109
|
-
@as("registering_event_src_address") registeringEventSrcAddress: Address.t,
|
|
110
|
-
@as("contract_address") contractAddress: Address.t,
|
|
106
|
+
@as("registration_block") registrationBlock: int,
|
|
107
|
+
// -1 when the address was registered from a block handler (no log index)
|
|
108
|
+
@as("registration_log_index") registrationLogIndex: int,
|
|
111
109
|
@as("contract_name") contractName: string,
|
|
112
110
|
}
|
|
113
111
|
|
|
112
|
+
// Extract the raw contract address from the composite id ({chainId}-{address}).
|
|
113
|
+
// Inverse of makeId. Keep in sync with makeId above and the SUBSTRING SQL in
|
|
114
|
+
// InternalTable.Chains.makeGetInitialStateQuery.
|
|
115
|
+
let getAddress = (entity: t): Address.t => {
|
|
116
|
+
let sepIdx = entity.id->String.indexOf("-")
|
|
117
|
+
entity.id
|
|
118
|
+
->String.slice(~start=sepIdx + 1, ~end=entity.id->String.length)
|
|
119
|
+
->Address.unsafeFromString
|
|
120
|
+
}
|
|
121
|
+
|
|
114
122
|
let schema = S.schema(s => {
|
|
115
123
|
id: s.matches(S.string),
|
|
116
124
|
chainId: s.matches(S.int),
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
registeringEventContractName: s.matches(S.string),
|
|
120
|
-
registeringEventName: s.matches(S.string),
|
|
121
|
-
registeringEventSrcAddress: s.matches(Address.schema),
|
|
122
|
-
registeringEventBlockTimestamp: s.matches(S.int),
|
|
123
|
-
contractAddress: s.matches(Address.schema),
|
|
125
|
+
registrationBlock: s.matches(S.int),
|
|
126
|
+
registrationLogIndex: s.matches(S.int),
|
|
124
127
|
contractName: s.matches(S.string),
|
|
125
128
|
})
|
|
126
129
|
|
|
@@ -131,13 +134,9 @@ module DynamicContractRegistry = {
|
|
|
131
134
|
~fields=[
|
|
132
135
|
Table.mkField("id", String, ~isPrimaryKey=true, ~fieldSchema=S.string),
|
|
133
136
|
Table.mkField("chain_id", Int32, ~fieldSchema=S.int),
|
|
134
|
-
Table.mkField("
|
|
135
|
-
|
|
136
|
-
Table.mkField("
|
|
137
|
-
Table.mkField("registering_event_contract_name", String, ~fieldSchema=S.string),
|
|
138
|
-
Table.mkField("registering_event_name", String, ~fieldSchema=S.string),
|
|
139
|
-
Table.mkField("registering_event_src_address", String, ~fieldSchema=Address.schema),
|
|
140
|
-
Table.mkField("contract_address", String, ~fieldSchema=Address.schema),
|
|
137
|
+
Table.mkField("registration_block", Int32, ~fieldSchema=S.int),
|
|
138
|
+
// -1 sentinel when registered from a block handler (no log index)
|
|
139
|
+
Table.mkField("registration_log_index", Int32, ~fieldSchema=S.int),
|
|
141
140
|
Table.mkField("contract_name", String, ~fieldSchema=S.string),
|
|
142
141
|
],
|
|
143
142
|
)
|
|
@@ -153,7 +152,6 @@ module DynamicContractRegistry = {
|
|
|
153
152
|
}->Internal.fromGenericEntityConfig
|
|
154
153
|
}
|
|
155
154
|
|
|
156
|
-
// Types for parsing source config from internal.config.json
|
|
157
155
|
type rpcSourceFor = | @as("sync") Sync | @as("fallback") Fallback | @as("live") Live
|
|
158
156
|
|
|
159
157
|
let rpcSourceForSchema = S.enum([Sync, Fallback, Live])
|
|
@@ -281,18 +279,18 @@ let entityJsonSchema = S.schema(s =>
|
|
|
281
279
|
|
|
282
280
|
let getFieldTypeAndSchema = (prop, ~enumConfigsByName: dict<Table.enumConfig<Table.enum>>) => {
|
|
283
281
|
let typ = prop["type"]
|
|
284
|
-
let isNullable = prop["isNullable"]->Option.
|
|
285
|
-
let isArray = prop["isArray"]->Option.
|
|
286
|
-
let isIndex = prop["isIndex"]->Option.
|
|
282
|
+
let isNullable = prop["isNullable"]->Option.getOr(false)
|
|
283
|
+
let isArray = prop["isArray"]->Option.getOr(false)
|
|
284
|
+
let isIndex = prop["isIndex"]->Option.getOr(false)
|
|
287
285
|
|
|
288
286
|
let (fieldType, baseSchema) = switch typ {
|
|
289
287
|
| "string" => (Table.String, S.string->S.toUnknown)
|
|
290
288
|
| "boolean" => (Table.Boolean, S.bool->S.toUnknown)
|
|
291
289
|
| "int" => (Table.Int32, S.int->S.toUnknown)
|
|
292
|
-
| "bigint" => (Table.BigInt({precision: ?prop["precision"]}), BigInt.schema->S.toUnknown)
|
|
290
|
+
| "bigint" => (Table.BigInt({precision: ?prop["precision"]}), Utils.BigInt.schema->S.toUnknown)
|
|
293
291
|
| "bigdecimal" => (
|
|
294
292
|
Table.BigDecimal({
|
|
295
|
-
config: ?prop["precision"]->Option.map(p => (p, prop["scale"]->Option.
|
|
293
|
+
config: ?(prop["precision"]->Option.map(p => (p, prop["scale"]->Option.getOr(0)))),
|
|
296
294
|
}),
|
|
297
295
|
BigDecimal.schema->S.toUnknown,
|
|
298
296
|
)
|
|
@@ -301,18 +299,27 @@ let getFieldTypeAndSchema = (prop, ~enumConfigsByName: dict<Table.enumConfig<Tab
|
|
|
301
299
|
| "json" => (Table.Json, S.json(~validate=false)->S.toUnknown)
|
|
302
300
|
| "date" => (Table.Date, Utils.Schema.dbDate->S.toUnknown)
|
|
303
301
|
| "enum" => {
|
|
304
|
-
let enumName = prop["enum"]->Option.
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
302
|
+
let enumName = prop["enum"]->Option.getOrThrow
|
|
303
|
+
|
|
304
|
+
// Build contracts for this chain from per-chain contract data + contract configs
|
|
305
|
+
|
|
306
|
+
// Get per-chain contract data (addresses, startBlock)
|
|
307
|
+
|
|
308
|
+
// Build event configs from JSON (field selections resolved inline)
|
|
309
|
+
|
|
310
|
+
// Build sourceConfig from the parsed chain config
|
|
311
|
+
|
|
312
|
+
// Build syncConfig from flattened fields
|
|
313
|
+
|
|
314
|
+
// Fuel doesn't have reorgs, SVM reorg handling is not supported
|
|
315
|
+
let enumConfig = enumConfigsByName->Dict.get(enumName)->Option.getOrThrow
|
|
309
316
|
(Table.Enum({config: enumConfig}), enumConfig.schema->S.toUnknown)
|
|
310
317
|
}
|
|
311
318
|
| "entity" => {
|
|
312
|
-
let entityName = prop["entity"]->Option.
|
|
319
|
+
let entityName = prop["entity"]->Option.getOrThrow
|
|
313
320
|
(Table.Entity({name: entityName}), S.string->S.toUnknown)
|
|
314
321
|
}
|
|
315
|
-
| other =>
|
|
322
|
+
| other => JsError.throwWithMessage("Unknown field type in entity config: " ++ other)
|
|
316
323
|
}
|
|
317
324
|
|
|
318
325
|
let fieldSchema = if isArray {
|
|
@@ -331,7 +338,7 @@ let getFieldTypeAndSchema = (prop, ~enumConfigsByName: dict<Table.enumConfig<Tab
|
|
|
331
338
|
|
|
332
339
|
let parseEnumsFromJson = (enumsJson: dict<array<string>>): array<Table.enumConfig<Table.enum>> => {
|
|
333
340
|
enumsJson
|
|
334
|
-
->
|
|
341
|
+
->Dict.toArray
|
|
335
342
|
->Array.map(((name, variants)) =>
|
|
336
343
|
Table.makeEnumConfig(~name, ~variants)->Table.fromGenericEnumConfig
|
|
337
344
|
)
|
|
@@ -341,7 +348,7 @@ let parseEntitiesFromJson = (
|
|
|
341
348
|
entitiesJson: array<'entityJson>,
|
|
342
349
|
~enumConfigsByName: dict<Table.enumConfig<Table.enum>>,
|
|
343
350
|
): array<Internal.entityConfig> => {
|
|
344
|
-
entitiesJson->Array.mapWithIndex((
|
|
351
|
+
entitiesJson->Array.mapWithIndex((entityJson, index) => {
|
|
345
352
|
let entityName = entityJson["name"]
|
|
346
353
|
|
|
347
354
|
let fields: array<Table.fieldOrDerived> = entityJson["properties"]->Array.map(prop => {
|
|
@@ -363,7 +370,7 @@ let parseEntitiesFromJson = (
|
|
|
363
370
|
|
|
364
371
|
let derivedFields: array<Table.fieldOrDerived> =
|
|
365
372
|
entityJson["derivedFields"]
|
|
366
|
-
->Option.
|
|
373
|
+
->Option.getOr([])
|
|
367
374
|
->Array.map(df =>
|
|
368
375
|
Table.mkDerivedFromField(
|
|
369
376
|
df["fieldName"],
|
|
@@ -374,7 +381,7 @@ let parseEntitiesFromJson = (
|
|
|
374
381
|
|
|
375
382
|
let compositeIndices =
|
|
376
383
|
entityJson["compositeIndices"]
|
|
377
|
-
->Option.
|
|
384
|
+
->Option.getOr([])
|
|
378
385
|
->Array.map(ci =>
|
|
379
386
|
ci->Array.map(
|
|
380
387
|
f => {
|
|
@@ -394,7 +401,7 @@ let parseEntitiesFromJson = (
|
|
|
394
401
|
// Use db field names (with _id suffix for linked entities) as schema locations
|
|
395
402
|
// to match the database column names used in Table.toSqlParams
|
|
396
403
|
let schema = S.schema(s => {
|
|
397
|
-
let dict =
|
|
404
|
+
let dict = Dict.make()
|
|
398
405
|
entityJson["properties"]->Array.forEach(
|
|
399
406
|
prop => {
|
|
400
407
|
let (_, fieldSchema, _, _, _) = getFieldTypeAndSchema(prop, ~enumConfigsByName)
|
|
@@ -402,7 +409,7 @@ let parseEntitiesFromJson = (
|
|
|
402
409
|
| Some(_) => prop["name"] ++ "_id"
|
|
403
410
|
| None => prop["name"]
|
|
404
411
|
}
|
|
405
|
-
dict->
|
|
412
|
+
dict->Dict.set(dbFieldName, s.matches(fieldSchema))
|
|
406
413
|
},
|
|
407
414
|
)
|
|
408
415
|
dict
|
|
@@ -420,6 +427,13 @@ let parseEntitiesFromJson = (
|
|
|
420
427
|
})
|
|
421
428
|
}
|
|
422
429
|
|
|
430
|
+
let publicConfigStorageSchema = S.schema(s =>
|
|
431
|
+
{
|
|
432
|
+
"postgres": s.matches(S.bool),
|
|
433
|
+
"clickhouse": s.matches(S.option(S.bool)),
|
|
434
|
+
}
|
|
435
|
+
)
|
|
436
|
+
|
|
423
437
|
let publicConfigSchema = S.schema(s =>
|
|
424
438
|
{
|
|
425
439
|
"name": s.matches(S.string),
|
|
@@ -430,6 +444,7 @@ let publicConfigSchema = S.schema(s =>
|
|
|
430
444
|
"rollbackOnReorg": s.matches(S.option(S.bool)),
|
|
431
445
|
"saveFullHistory": s.matches(S.option(S.bool)),
|
|
432
446
|
"rawEvents": s.matches(S.option(S.bool)),
|
|
447
|
+
"storage": s.matches(publicConfigStorageSchema),
|
|
433
448
|
"evm": s.matches(S.option(publicConfigEvmSchema)),
|
|
434
449
|
"fuel": s.matches(S.option(publicConfigEcosystemSchema)),
|
|
435
450
|
"svm": s.matches(S.option(publicConfigEcosystemSchema)),
|
|
@@ -438,12 +453,13 @@ let publicConfigSchema = S.schema(s =>
|
|
|
438
453
|
}
|
|
439
454
|
)
|
|
440
455
|
|
|
441
|
-
let fromPublic = (publicConfigJson:
|
|
456
|
+
let fromPublic = (publicConfigJson: JSON.t) => {
|
|
457
|
+
let maxAddrInPartition = Env.maxAddrInPartition
|
|
442
458
|
// Parse public config
|
|
443
459
|
let publicConfig = try publicConfigJson->S.parseOrThrow(publicConfigSchema) catch {
|
|
444
460
|
| S.Raised(exn) =>
|
|
445
|
-
|
|
446
|
-
`Invalid
|
|
461
|
+
JsError.throwWithMessage(
|
|
462
|
+
`Invalid indexer config: ${exn->Utils.prettifyExn->(Utils.magic: exn => string)}`,
|
|
447
463
|
)
|
|
448
464
|
}
|
|
449
465
|
|
|
@@ -457,16 +473,22 @@ let fromPublic = (publicConfigJson: Js.Json.t, ~maxAddrInPartition=5000) => {
|
|
|
457
473
|
| (None, Some(ecosystemConfig), None) => (ecosystemConfig["chains"], Ecosystem.Fuel)
|
|
458
474
|
| (None, None, Some(ecosystemConfig)) => (ecosystemConfig["chains"], Ecosystem.Svm)
|
|
459
475
|
| (None, None, None) =>
|
|
460
|
-
|
|
476
|
+
JsError.throwWithMessage("Invalid indexer config: No ecosystem configured (evm, fuel, or svm)")
|
|
461
477
|
| _ =>
|
|
462
|
-
|
|
478
|
+
JsError.throwWithMessage(
|
|
463
479
|
"Invalid indexer config: Multiple ecosystems are not supported for a single indexer",
|
|
464
480
|
)
|
|
465
481
|
}
|
|
466
482
|
|
|
483
|
+
let ecosystem = switch ecosystemName {
|
|
484
|
+
| Ecosystem.Evm => Evm.ecosystem
|
|
485
|
+
| Ecosystem.Fuel => Fuel.ecosystem
|
|
486
|
+
| Ecosystem.Svm => Svm.ecosystem
|
|
487
|
+
}
|
|
488
|
+
|
|
467
489
|
// Extract EVM-specific options with defaults
|
|
468
490
|
let lowercaseAddresses = switch publicConfig["evm"] {
|
|
469
|
-
| Some(evm) => evm["addressFormat"]->Option.
|
|
491
|
+
| Some(evm) => evm["addressFormat"]->Option.getOr(Checksum) == Lowercase
|
|
470
492
|
| None => false
|
|
471
493
|
}
|
|
472
494
|
|
|
@@ -483,32 +505,32 @@ let fromPublic = (publicConfigJson: Js.Json.t, ~maxAddrInPartition=5000) => {
|
|
|
483
505
|
Utils.Set.fromArray(
|
|
484
506
|
Array.concat(
|
|
485
507
|
EventConfigBuilder.alwaysIncludedBlockFields,
|
|
486
|
-
evm["globalBlockFields"]->Option.
|
|
508
|
+
evm["globalBlockFields"]->Option.getOr([]),
|
|
487
509
|
),
|
|
488
510
|
),
|
|
489
|
-
Utils.Set.fromArray(evm["globalTransactionFields"]->Option.
|
|
511
|
+
Utils.Set.fromArray(evm["globalTransactionFields"]->Option.getOr([])),
|
|
490
512
|
)
|
|
491
513
|
| None => (Utils.Set.fromArray(EventConfigBuilder.alwaysIncludedBlockFields), Utils.Set.make())
|
|
492
514
|
}
|
|
493
515
|
|
|
494
516
|
// Build contract data lookup: ABI, event signatures, event configs (keyed by capitalized name)
|
|
495
|
-
let contractDataByName:
|
|
517
|
+
let contractDataByName: dict<{
|
|
496
518
|
"abi": EvmTypes.Abi.t,
|
|
497
519
|
"eventSignatures": array<string>,
|
|
498
520
|
"events": option<array<_>>,
|
|
499
|
-
}> =
|
|
521
|
+
}> = Dict.make()
|
|
500
522
|
switch publicContractsConfig {
|
|
501
523
|
| Some(contractsDict) =>
|
|
502
524
|
contractsDict
|
|
503
|
-
->
|
|
525
|
+
->Dict.toArray
|
|
504
526
|
->Array.forEach(((contractName, contractConfig)) => {
|
|
505
527
|
let capitalizedName = contractName->Utils.String.capitalize
|
|
506
|
-
let abi = contractConfig["abi"]->(Utils.magic:
|
|
528
|
+
let abi = contractConfig["abi"]->(Utils.magic: JSON.t => EvmTypes.Abi.t)
|
|
507
529
|
let eventSignatures = switch contractConfig["events"] {
|
|
508
530
|
| Some(events) => events->Array.map(eventItem => eventItem["event"])
|
|
509
531
|
| None => []
|
|
510
532
|
}
|
|
511
|
-
contractDataByName->
|
|
533
|
+
contractDataByName->Dict.set(
|
|
512
534
|
capitalizedName,
|
|
513
535
|
{"abi": abi, "eventSignatures": eventSignatures, "events": contractConfig["events"]},
|
|
514
536
|
)
|
|
@@ -517,19 +539,21 @@ let fromPublic = (publicConfigJson: Js.Json.t, ~maxAddrInPartition=5000) => {
|
|
|
517
539
|
}
|
|
518
540
|
|
|
519
541
|
// Build event configs for a contract from JSON event items
|
|
520
|
-
let buildContractEvents = (
|
|
542
|
+
let buildContractEvents = (
|
|
543
|
+
~contractName,
|
|
544
|
+
~events: option<array<_>>,
|
|
545
|
+
~abi,
|
|
546
|
+
~chainId: int,
|
|
547
|
+
~startBlock: option<int>,
|
|
548
|
+
) => {
|
|
521
549
|
switch events {
|
|
522
550
|
| None => []
|
|
523
551
|
| Some(eventItems) =>
|
|
524
552
|
eventItems->Array.map(eventItem => {
|
|
525
553
|
let eventName = eventItem["name"]
|
|
526
554
|
let sighash = eventItem["sighash"]
|
|
527
|
-
let params = eventItem["params"]->Option.
|
|
555
|
+
let params = eventItem["params"]->Option.getOr([])
|
|
528
556
|
let kind = eventItem["kind"]
|
|
529
|
-
// Get handler registration data
|
|
530
|
-
let isWildcard = HandlerRegister.isWildcard(~contractName, ~eventName)
|
|
531
|
-
let handler = HandlerRegister.getHandler(~contractName, ~eventName)
|
|
532
|
-
let contractRegister = HandlerRegister.getContractRegister(~contractName, ~eventName)
|
|
533
557
|
|
|
534
558
|
switch ecosystemName {
|
|
535
559
|
| Ecosystem.Fuel =>
|
|
@@ -540,13 +564,14 @@ let fromPublic = (publicConfigJson: Js.Json.t, ~maxAddrInPartition=5000) => {
|
|
|
540
564
|
~eventName,
|
|
541
565
|
~kind=fuelKind,
|
|
542
566
|
~sighash,
|
|
543
|
-
~rawAbi=abi->(Utils.magic: EvmTypes.Abi.t =>
|
|
544
|
-
~isWildcard,
|
|
545
|
-
~handler,
|
|
546
|
-
~contractRegister,
|
|
567
|
+
~rawAbi=abi->(Utils.magic: EvmTypes.Abi.t => JSON.t),
|
|
568
|
+
~isWildcard=false,
|
|
569
|
+
~handler=None,
|
|
570
|
+
~contractRegister=None,
|
|
571
|
+
~startBlock?,
|
|
547
572
|
) :> Internal.eventConfig)
|
|
548
573
|
| None =>
|
|
549
|
-
|
|
574
|
+
JsError.throwWithMessage(
|
|
550
575
|
`Fuel event ${contractName}.${eventName} is missing "kind" in internal config`,
|
|
551
576
|
)
|
|
552
577
|
}
|
|
@@ -556,12 +581,15 @@ let fromPublic = (publicConfigJson: Js.Json.t, ~maxAddrInPartition=5000) => {
|
|
|
556
581
|
~eventName,
|
|
557
582
|
~sighash,
|
|
558
583
|
~params,
|
|
559
|
-
~isWildcard,
|
|
560
|
-
~handler,
|
|
561
|
-
~contractRegister,
|
|
562
|
-
~eventFilters=
|
|
584
|
+
~isWildcard=false,
|
|
585
|
+
~handler=None,
|
|
586
|
+
~contractRegister=None,
|
|
587
|
+
~eventFilters=None,
|
|
588
|
+
~probeChainId=chainId,
|
|
589
|
+
~onEventBlockFilterSchema=ecosystem.onEventBlockFilterSchema,
|
|
563
590
|
~blockFields=?eventItem["blockFields"],
|
|
564
591
|
~transactionFields=?eventItem["transactionFields"],
|
|
592
|
+
~startBlock?,
|
|
565
593
|
~globalBlockFieldsSet,
|
|
566
594
|
~globalTransactionFieldsSet,
|
|
567
595
|
) :> Internal.eventConfig)
|
|
@@ -595,31 +623,35 @@ let fromPublic = (publicConfigJson: Js.Json.t, ~maxAddrInPartition=5000) => {
|
|
|
595
623
|
// Build chains from JSON config (no more codegenChains)
|
|
596
624
|
let chains =
|
|
597
625
|
publicChainsConfig
|
|
598
|
-
->
|
|
599
|
-
->
|
|
600
|
-
let publicChainConfig = publicChainsConfig->
|
|
626
|
+
->Dict.keysToArray
|
|
627
|
+
->Array.map(chainName => {
|
|
628
|
+
let publicChainConfig = publicChainsConfig->Dict.getUnsafe(chainName)
|
|
601
629
|
let chainId = publicChainConfig["id"]
|
|
602
630
|
|
|
603
|
-
|
|
604
|
-
let chainContracts = publicChainConfig["contracts"]->Option.getWithDefault(Js.Dict.empty())
|
|
631
|
+
let chainContracts = publicChainConfig["contracts"]->Option.getOr(Dict.make())
|
|
605
632
|
let contracts =
|
|
606
633
|
contractDataByName
|
|
607
|
-
->
|
|
634
|
+
->Dict.toArray
|
|
608
635
|
->Array.map(((capitalizedName, contractData)) => {
|
|
609
|
-
|
|
610
|
-
let chainContract = chainContracts->Js.Dict.get(capitalizedName)
|
|
636
|
+
let chainContract = chainContracts->Dict.get(capitalizedName)
|
|
611
637
|
let addresses =
|
|
612
638
|
chainContract
|
|
613
639
|
->Option.flatMap(cc => cc["addresses"])
|
|
614
|
-
->Option.
|
|
640
|
+
->Option.getOr([])
|
|
615
641
|
->Array.map(parseAddress)
|
|
616
642
|
let startBlock = chainContract->Option.flatMap(cc => cc["startBlock"])
|
|
617
643
|
|
|
618
644
|
// Build event configs from JSON (field selections resolved inline)
|
|
645
|
+
// chainId is threaded in so the where-callback detection probe
|
|
646
|
+
// exercises the callback with this chain's real id — handlers
|
|
647
|
+
// that branch on `chain.id` are taken through the same path
|
|
648
|
+
// they will follow at runtime.
|
|
619
649
|
let events = buildContractEvents(
|
|
620
650
|
~contractName=capitalizedName,
|
|
621
651
|
~events=contractData["events"],
|
|
622
652
|
~abi=contractData["abi"],
|
|
653
|
+
~chainId,
|
|
654
|
+
~startBlock,
|
|
623
655
|
)
|
|
624
656
|
|
|
625
657
|
{
|
|
@@ -632,14 +664,12 @@ let fromPublic = (publicConfigJson: Js.Json.t, ~maxAddrInPartition=5000) => {
|
|
|
632
664
|
}
|
|
633
665
|
})
|
|
634
666
|
|
|
635
|
-
// Build sourceConfig from the parsed chain config
|
|
636
667
|
let sourceConfig = switch ecosystemName {
|
|
637
668
|
| Ecosystem.Evm =>
|
|
638
669
|
let rpcs =
|
|
639
670
|
publicChainConfig["rpcs"]
|
|
640
|
-
->Option.
|
|
671
|
+
->Option.getOr([])
|
|
641
672
|
->Array.map((rpcConfig): evmRpcConfig => {
|
|
642
|
-
// Build syncConfig from flattened fields
|
|
643
673
|
let initialBlockInterval = rpcConfig["initialBlockInterval"]
|
|
644
674
|
let backoffMultiplicative = rpcConfig["backoffMultiplicative"]
|
|
645
675
|
let accelerationAdditive = rpcConfig["accelerationAdditive"]
|
|
@@ -682,12 +712,13 @@ let fromPublic = (publicConfigJson: Js.Json.t, ~maxAddrInPartition=5000) => {
|
|
|
682
712
|
| Ecosystem.Fuel =>
|
|
683
713
|
switch publicChainConfig["hypersync"] {
|
|
684
714
|
| Some(hypersync) => FuelSourceConfig({hypersync: hypersync})
|
|
685
|
-
| None =>
|
|
715
|
+
| None =>
|
|
716
|
+
JsError.throwWithMessage(`Chain ${chainName} is missing hypersync endpoint in config`)
|
|
686
717
|
}
|
|
687
718
|
| Ecosystem.Svm =>
|
|
688
719
|
switch publicChainConfig["rpc"] {
|
|
689
720
|
| Some(rpc) => SvmSourceConfig({rpc: rpc})
|
|
690
|
-
| None =>
|
|
721
|
+
| None => JsError.throwWithMessage(`Chain ${chainName} is missing rpc endpoint in config`)
|
|
691
722
|
}
|
|
692
723
|
}
|
|
693
724
|
|
|
@@ -697,11 +728,11 @@ let fromPublic = (publicConfigJson: Js.Json.t, ~maxAddrInPartition=5000) => {
|
|
|
697
728
|
startBlock: publicChainConfig["startBlock"],
|
|
698
729
|
endBlock: ?publicChainConfig["endBlock"],
|
|
699
730
|
maxReorgDepth: switch ecosystemName {
|
|
700
|
-
| Ecosystem.Evm => publicChainConfig["maxReorgDepth"]->Option.
|
|
701
|
-
|
|
731
|
+
| Ecosystem.Evm => publicChainConfig["maxReorgDepth"]->Option.getOr(200)
|
|
732
|
+
|
|
702
733
|
| Ecosystem.Fuel | Ecosystem.Svm => 0
|
|
703
734
|
},
|
|
704
|
-
blockLag: publicChainConfig["blockLag"]->Option.
|
|
735
|
+
blockLag: publicChainConfig["blockLag"]->Option.getOr(0),
|
|
705
736
|
contracts,
|
|
706
737
|
sourceConfig,
|
|
707
738
|
}
|
|
@@ -709,57 +740,40 @@ let fromPublic = (publicConfigJson: Js.Json.t, ~maxAddrInPartition=5000) => {
|
|
|
709
740
|
|
|
710
741
|
let chainMap =
|
|
711
742
|
chains
|
|
712
|
-
->
|
|
743
|
+
->Array.map(chain => {
|
|
713
744
|
(ChainMap.Chain.makeUnsafe(~chainId=chain.id), chain)
|
|
714
745
|
})
|
|
715
746
|
->ChainMap.fromArrayUnsafe
|
|
716
747
|
|
|
717
|
-
// Build the contract name mapping for efficient lookup
|
|
718
|
-
let addContractNameToContractNameMapping = Js.Dict.empty()
|
|
719
|
-
chains->Array.forEach(chainConfig => {
|
|
720
|
-
chainConfig.contracts->Array.forEach(contract => {
|
|
721
|
-
let addKey = "add" ++ contract.name->Utils.String.capitalize
|
|
722
|
-
addContractNameToContractNameMapping->Js.Dict.set(addKey, contract.name)
|
|
723
|
-
})
|
|
724
|
-
})
|
|
725
|
-
|
|
726
|
-
let ecosystem = switch ecosystemName {
|
|
727
|
-
| Ecosystem.Evm => Evm.ecosystem
|
|
728
|
-
| Ecosystem.Fuel => Fuel.ecosystem
|
|
729
|
-
| Ecosystem.Svm => Svm.ecosystem
|
|
730
|
-
}
|
|
731
|
-
|
|
732
748
|
// Parse enums and entities from JSON config
|
|
733
749
|
let allEnums =
|
|
734
750
|
publicConfig["enums"]
|
|
735
|
-
->Option.
|
|
751
|
+
->Option.getOr(Dict.make())
|
|
736
752
|
->parseEnumsFromJson
|
|
737
753
|
|
|
738
754
|
let enumConfigsByName =
|
|
739
|
-
allEnums
|
|
740
|
-
->Js.Array2.map(enumConfig => (enumConfig.name, enumConfig))
|
|
741
|
-
->Js.Dict.fromArray
|
|
755
|
+
allEnums->Array.map(enumConfig => (enumConfig.name, enumConfig))->Dict.fromArray
|
|
742
756
|
|
|
743
757
|
let userEntities =
|
|
744
758
|
publicConfig["entities"]
|
|
745
|
-
->Option.
|
|
759
|
+
->Option.getOr([])
|
|
746
760
|
->parseEntitiesFromJson(~enumConfigsByName)
|
|
747
761
|
|
|
748
|
-
let allEntities = userEntities->
|
|
762
|
+
let allEntities = userEntities->Array.concat([EnvioAddresses.entityConfig])
|
|
749
763
|
|
|
750
764
|
let userEntitiesByName =
|
|
751
765
|
userEntities
|
|
752
|
-
->
|
|
766
|
+
->Array.map(entityConfig => {
|
|
753
767
|
(entityConfig.name, entityConfig)
|
|
754
768
|
})
|
|
755
|
-
->
|
|
769
|
+
->Dict.fromArray
|
|
756
770
|
|
|
757
771
|
// Extract contract handlers from the public config
|
|
758
772
|
let contractHandlers = switch publicContractsConfig {
|
|
759
773
|
| Some(contractsDict) =>
|
|
760
774
|
contractsDict
|
|
761
|
-
->
|
|
762
|
-
->
|
|
775
|
+
->Dict.toArray
|
|
776
|
+
->Array.map(((contractName, contractConfig)) => {
|
|
763
777
|
{
|
|
764
778
|
name: contractName->Utils.String.capitalize,
|
|
765
779
|
handler: contractConfig["handler"],
|
|
@@ -771,19 +785,22 @@ let fromPublic = (publicConfigJson: Js.Json.t, ~maxAddrInPartition=5000) => {
|
|
|
771
785
|
{
|
|
772
786
|
name: publicConfig["name"],
|
|
773
787
|
description: publicConfig["description"],
|
|
774
|
-
handlers: publicConfig["handlers"]->Option.
|
|
788
|
+
handlers: publicConfig["handlers"]->Option.getOr("src/handlers"),
|
|
775
789
|
contractHandlers,
|
|
776
|
-
shouldRollbackOnReorg: publicConfig["rollbackOnReorg"]->Option.
|
|
777
|
-
shouldSaveFullHistory: publicConfig["saveFullHistory"]->Option.
|
|
778
|
-
|
|
790
|
+
shouldRollbackOnReorg: publicConfig["rollbackOnReorg"]->Option.getOr(true),
|
|
791
|
+
shouldSaveFullHistory: publicConfig["saveFullHistory"]->Option.getOr(false),
|
|
792
|
+
storage: {
|
|
793
|
+
postgres: publicConfig["storage"]["postgres"],
|
|
794
|
+
clickhouse: publicConfig["storage"]["clickhouse"]->Option.getOr(false),
|
|
795
|
+
},
|
|
796
|
+
multichain: publicConfig["multichain"]->Option.getOr(Unordered),
|
|
779
797
|
chainMap,
|
|
780
798
|
defaultChain: chains->Array.get(0),
|
|
781
|
-
enableRawEvents: publicConfig["rawEvents"]->Option.
|
|
799
|
+
enableRawEvents: publicConfig["rawEvents"]->Option.getOr(false),
|
|
782
800
|
ecosystem,
|
|
783
801
|
maxAddrInPartition,
|
|
784
|
-
batchSize: publicConfig["fullBatchSize"]->Option.
|
|
802
|
+
batchSize: publicConfig["fullBatchSize"]->Option.getOr(5000),
|
|
785
803
|
lowercaseAddresses,
|
|
786
|
-
addContractNameToContractNameMapping,
|
|
787
804
|
userEntitiesByName,
|
|
788
805
|
userEntities,
|
|
789
806
|
allEntities,
|
|
@@ -791,18 +808,32 @@ let fromPublic = (publicConfigJson: Js.Json.t, ~maxAddrInPartition=5000) => {
|
|
|
791
808
|
}
|
|
792
809
|
}
|
|
793
810
|
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
811
|
+
// Look up an event config by (contract, event) name. When `chainId` is given,
|
|
812
|
+
// returns that chain's per-chain event config (matters for where-callback
|
|
813
|
+
// probe detection, which runs with the chain's real id). Without `chainId`,
|
|
814
|
+
// falls back to the first chain that declares this event.
|
|
815
|
+
let getEventConfig = (config: t, ~contractName, ~eventName, ~chainId: option<int>=?) => {
|
|
816
|
+
let chains = switch chainId {
|
|
817
|
+
| Some(chainId) =>
|
|
818
|
+
let chain = ChainMap.Chain.makeUnsafe(~chainId)
|
|
819
|
+
switch config.chainMap->ChainMap.get(chain) {
|
|
820
|
+
| chainConfig => [chainConfig]
|
|
821
|
+
| exception _ =>
|
|
822
|
+
JsError.throwWithMessage(
|
|
823
|
+
`Chain ${chainId->Int.toString} is not configured. Add it to config.yaml or pass a configured chain.`,
|
|
824
|
+
)
|
|
825
|
+
}
|
|
826
|
+
| None => config.chainMap->ChainMap.values
|
|
827
|
+
}
|
|
828
|
+
chains->Array.reduce(None, (acc, chain) => {
|
|
798
829
|
switch acc {
|
|
799
830
|
| Some(_) => acc
|
|
800
831
|
| None =>
|
|
801
832
|
chain.contracts
|
|
802
|
-
->
|
|
803
|
-
->Belt.Option.flatMap(contract => contract.events->
|
|
833
|
+
->Array.find(c => c.name == contractName)
|
|
834
|
+
->Belt.Option.flatMap(contract => contract.events->Array.find(e => e.name == eventName))
|
|
804
835
|
}
|
|
805
|
-
}
|
|
836
|
+
})
|
|
806
837
|
}
|
|
807
838
|
|
|
808
839
|
let shouldSaveHistory = (config, ~isInReorgThreshold) =>
|
|
@@ -815,7 +846,34 @@ let getChain = (config, ~chainId) => {
|
|
|
815
846
|
let chain = ChainMap.Chain.makeUnsafe(~chainId)
|
|
816
847
|
config.chainMap->ChainMap.has(chain)
|
|
817
848
|
? chain
|
|
818
|
-
:
|
|
849
|
+
: JsError.throwWithMessage(
|
|
819
850
|
"No chain with id " ++ chain->ChainMap.Chain.toString ++ " found in config.yaml",
|
|
820
851
|
)
|
|
821
852
|
}
|
|
853
|
+
|
|
854
|
+
// A CLI command payload already contains the resolved JSON; priming lets
|
|
855
|
+
// downstream callers skip the NAPI `getConfigJson` round-trip. Calling
|
|
856
|
+
// `prime` again invalidates the memo.
|
|
857
|
+
%%private(let primedJson: ref<option<JSON.t>> = ref(None))
|
|
858
|
+
%%private(let cached: ref<option<t>> = ref(None))
|
|
859
|
+
let prime = (json: JSON.t): unit => {
|
|
860
|
+
primedJson := Some(json)
|
|
861
|
+
cached := None
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// The returned value is a pure function of the JSON — no handler
|
|
865
|
+
// registrations are applied here. Post-registration configs come from
|
|
866
|
+
// `HandlerLoader.applyRegistrations`. That purity is what lets this
|
|
867
|
+
// memoize without invalidation.
|
|
868
|
+
let loadWithoutRegistrations = () =>
|
|
869
|
+
switch cached.contents {
|
|
870
|
+
| Some(c) => c
|
|
871
|
+
| None => {
|
|
872
|
+
let c = switch primedJson.contents {
|
|
873
|
+
| Some(json) => json->fromPublic
|
|
874
|
+
| None => Core.getConfigJson()->JSON.parseOrThrow->fromPublic
|
|
875
|
+
}
|
|
876
|
+
cached := Some(c)
|
|
877
|
+
c
|
|
878
|
+
}
|
|
879
|
+
}
|