envio 3.0.0-alpha.0 → 3.0.0-alpha.10
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 +53 -57
- package/fuel.schema.json +35 -31
- package/index.d.ts +446 -1
- package/index.js +4 -0
- package/package.json +23 -11
- package/rescript.json +4 -1
- package/src/Batch.res.mjs +1 -1
- package/src/Benchmark.res +394 -0
- package/src/Benchmark.res.mjs +398 -0
- package/src/ChainFetcher.res +526 -0
- package/src/ChainFetcher.res.mjs +343 -0
- package/src/ChainManager.res +182 -0
- package/src/ChainManager.res.mjs +150 -0
- package/src/Config.res +367 -27
- package/src/Config.res.mjs +338 -28
- package/src/{Indexer.res → Ctx.res} +1 -1
- package/src/Ecosystem.res +25 -0
- package/src/Ecosystem.res.mjs +29 -0
- package/src/Env.res +252 -0
- package/src/Env.res.mjs +271 -0
- package/src/Envio.gen.ts +9 -1
- package/src/Envio.res +12 -9
- package/src/EventProcessing.res +476 -0
- package/src/EventProcessing.res.mjs +341 -0
- package/src/EventRegister.res +4 -15
- package/src/EventRegister.res.mjs +3 -9
- package/src/EventRegister.resi +2 -8
- package/src/FetchState.res +54 -29
- package/src/FetchState.res.mjs +62 -35
- package/src/GlobalState.res +1163 -0
- package/src/GlobalState.res.mjs +1196 -0
- package/src/GlobalStateManager.res +68 -0
- package/src/GlobalStateManager.res.mjs +75 -0
- package/src/GlobalStateManager.resi +7 -0
- package/src/HandlerLoader.res +89 -0
- package/src/HandlerLoader.res.mjs +79 -0
- package/src/Internal.gen.ts +3 -14
- package/src/Internal.res +45 -13
- package/src/LoadLayer.res +444 -0
- package/src/LoadLayer.res.mjs +296 -0
- package/src/LoadLayer.resi +32 -0
- package/src/LogSelection.res +33 -27
- package/src/LogSelection.res.mjs +6 -0
- package/src/Logging.res +11 -4
- package/src/Logging.res.mjs +14 -6
- package/src/Main.res +351 -0
- package/src/Main.res.mjs +312 -0
- package/src/Persistence.res +1 -2
- package/src/PgStorage.gen.ts +10 -0
- package/src/PgStorage.res +28 -34
- package/src/PgStorage.res.d.mts +5 -0
- package/src/PgStorage.res.mjs +27 -27
- package/src/Prometheus.res +8 -8
- package/src/Prometheus.res.mjs +10 -10
- package/src/ReorgDetection.res +6 -10
- package/src/ReorgDetection.res.mjs +6 -6
- package/src/TestIndexer.res +536 -0
- package/src/TestIndexer.res.mjs +412 -0
- package/src/TestIndexerProxyStorage.res +210 -0
- package/src/TestIndexerProxyStorage.res.mjs +157 -0
- package/src/Types.ts +1 -1
- package/src/UserContext.res +355 -0
- package/src/UserContext.res.mjs +236 -0
- package/src/Utils.res +28 -0
- package/src/Utils.res.mjs +18 -0
- package/src/bindings/ClickHouse.res +31 -1
- package/src/bindings/ClickHouse.res.mjs +27 -1
- package/src/bindings/DateFns.res +71 -0
- package/src/bindings/DateFns.res.mjs +22 -0
- package/src/bindings/Ethers.res +27 -67
- package/src/bindings/Ethers.res.mjs +18 -70
- package/src/bindings/EventSource.res +13 -0
- package/src/bindings/EventSource.res.mjs +2 -0
- package/src/bindings/NodeJs.res +44 -3
- package/src/bindings/NodeJs.res.mjs +11 -3
- package/src/bindings/Pino.res +21 -7
- package/src/bindings/Pino.res.mjs +11 -5
- package/src/bindings/Postgres.gen.ts +8 -0
- package/src/bindings/Postgres.res +3 -0
- package/src/bindings/Postgres.res.d.mts +5 -0
- package/src/bindings/RescriptMocha.res +123 -0
- package/src/bindings/RescriptMocha.res.mjs +18 -0
- package/src/bindings/Vitest.res +134 -0
- package/src/bindings/Vitest.res.mjs +9 -0
- package/src/bindings/Yargs.res +8 -0
- package/src/bindings/Yargs.res.mjs +2 -0
- package/src/db/InternalTable.res +2 -0
- package/src/db/InternalTable.res.mjs +1 -1
- package/src/sources/Evm.res +87 -0
- package/src/sources/Evm.res.mjs +105 -0
- package/src/sources/EvmChain.res +90 -0
- package/src/sources/EvmChain.res.mjs +57 -0
- package/src/sources/Fuel.res +19 -34
- package/src/sources/Fuel.res.mjs +34 -16
- package/src/sources/FuelSDK.res +38 -0
- package/src/sources/FuelSDK.res.mjs +29 -0
- package/src/sources/HyperFuel.res +2 -2
- package/src/sources/HyperFuel.resi +1 -1
- package/src/sources/HyperFuelClient.res +2 -2
- package/src/sources/HyperFuelSource.res +8 -8
- package/src/sources/HyperFuelSource.res.mjs +5 -5
- package/src/sources/HyperSyncHeightStream.res +97 -0
- package/src/sources/HyperSyncHeightStream.res.mjs +94 -0
- package/src/sources/HyperSyncSource.res +64 -172
- package/src/sources/HyperSyncSource.res.mjs +73 -155
- package/src/sources/Rpc.res +43 -0
- package/src/sources/Rpc.res.mjs +31 -0
- package/src/sources/RpcSource.res +32 -130
- package/src/sources/RpcSource.res.mjs +47 -121
- package/src/sources/Source.res +3 -2
- package/src/sources/SourceManager.res +183 -108
- package/src/sources/SourceManager.res.mjs +162 -99
- package/src/sources/SourceManager.resi +4 -5
- package/src/sources/Svm.res +59 -0
- package/src/sources/Svm.res.mjs +79 -0
- package/src/tui/Tui.res +266 -0
- package/src/tui/Tui.res.mjs +342 -0
- package/src/tui/bindings/Ink.res +376 -0
- package/src/tui/bindings/Ink.res.mjs +75 -0
- package/src/tui/bindings/Style.res +123 -0
- package/src/tui/bindings/Style.res.mjs +2 -0
- package/src/tui/components/BufferedProgressBar.res +40 -0
- package/src/tui/components/BufferedProgressBar.res.mjs +57 -0
- package/src/tui/components/CustomHooks.res +122 -0
- package/src/tui/components/CustomHooks.res.mjs +179 -0
- package/src/tui/components/Messages.res +41 -0
- package/src/tui/components/Messages.res.mjs +75 -0
- package/src/tui/components/SyncETA.res +193 -0
- package/src/tui/components/SyncETA.res.mjs +269 -0
- package/src/tui/components/TuiData.res +46 -0
- package/src/tui/components/TuiData.res.mjs +29 -0
- package/src/Platform.res +0 -140
- package/src/Platform.res.mjs +0 -170
- package/src/bindings/Ethers.gen.ts +0 -14
- /package/src/{Indexer.res.mjs → Ctx.res.mjs} +0 -0
package/src/Main.res
ADDED
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
open Belt
|
|
2
|
+
|
|
3
|
+
type chainData = {
|
|
4
|
+
chainId: float,
|
|
5
|
+
poweredByHyperSync: bool,
|
|
6
|
+
firstEventBlockNumber: option<int>,
|
|
7
|
+
latestProcessedBlock: option<int>,
|
|
8
|
+
timestampCaughtUpToHeadOrEndblock: option<Js.Date.t>,
|
|
9
|
+
numEventsProcessed: int,
|
|
10
|
+
latestFetchedBlockNumber: int,
|
|
11
|
+
// Need this for API backwards compatibility
|
|
12
|
+
@as("currentBlockHeight")
|
|
13
|
+
knownHeight: int,
|
|
14
|
+
numBatchesFetched: int,
|
|
15
|
+
endBlock: option<int>,
|
|
16
|
+
numAddresses: int,
|
|
17
|
+
}
|
|
18
|
+
@tag("status")
|
|
19
|
+
type state =
|
|
20
|
+
| @as("disabled") Disabled({})
|
|
21
|
+
| @as("initializing") Initializing({})
|
|
22
|
+
| @as("active")
|
|
23
|
+
Active({
|
|
24
|
+
envioVersion: string,
|
|
25
|
+
chains: array<chainData>,
|
|
26
|
+
indexerStartTime: Js.Date.t,
|
|
27
|
+
isPreRegisteringDynamicContracts: bool,
|
|
28
|
+
isUnorderedMultichainMode: bool,
|
|
29
|
+
rollbackOnReorg: bool,
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
let chainDataSchema = S.schema((s): chainData => {
|
|
33
|
+
chainId: s.matches(S.float),
|
|
34
|
+
poweredByHyperSync: s.matches(S.bool),
|
|
35
|
+
firstEventBlockNumber: s.matches(S.option(S.int)),
|
|
36
|
+
latestProcessedBlock: s.matches(S.option(S.int)),
|
|
37
|
+
timestampCaughtUpToHeadOrEndblock: s.matches(S.option(S.datetime(S.string))),
|
|
38
|
+
numEventsProcessed: s.matches(S.int),
|
|
39
|
+
latestFetchedBlockNumber: s.matches(S.int),
|
|
40
|
+
knownHeight: s.matches(S.int),
|
|
41
|
+
numBatchesFetched: s.matches(S.int),
|
|
42
|
+
endBlock: s.matches(S.option(S.int)),
|
|
43
|
+
numAddresses: s.matches(S.int),
|
|
44
|
+
})
|
|
45
|
+
let stateSchema = S.union([
|
|
46
|
+
S.literal(Disabled({})),
|
|
47
|
+
S.literal(Initializing({})),
|
|
48
|
+
S.schema(s => Active({
|
|
49
|
+
envioVersion: s.matches(S.string),
|
|
50
|
+
chains: s.matches(S.array(chainDataSchema)),
|
|
51
|
+
indexerStartTime: s.matches(S.datetime(S.string)),
|
|
52
|
+
// Keep the field, since Dev Console expects it to be present
|
|
53
|
+
isPreRegisteringDynamicContracts: false,
|
|
54
|
+
isUnorderedMultichainMode: s.matches(S.bool),
|
|
55
|
+
rollbackOnReorg: s.matches(S.bool),
|
|
56
|
+
})),
|
|
57
|
+
])
|
|
58
|
+
|
|
59
|
+
let globalGsManagerRef: ref<option<GlobalStateManager.t>> = ref(None)
|
|
60
|
+
|
|
61
|
+
let getGlobalIndexer = (~config: Config.t): 'indexer => {
|
|
62
|
+
let indexer = Utils.Object.createNullObject()
|
|
63
|
+
|
|
64
|
+
indexer
|
|
65
|
+
->Utils.Object.definePropertyWithValue("name", {enumerable: true, value: config.name})
|
|
66
|
+
->Utils.Object.definePropertyWithValue(
|
|
67
|
+
"description",
|
|
68
|
+
{enumerable: true, value: config.description},
|
|
69
|
+
)
|
|
70
|
+
->ignore
|
|
71
|
+
|
|
72
|
+
let chainIds = []
|
|
73
|
+
|
|
74
|
+
// Build chains object with chain ID as string key
|
|
75
|
+
let chains = Utils.Object.createNullObject()
|
|
76
|
+
config.chainMap
|
|
77
|
+
->ChainMap.values
|
|
78
|
+
->Array.forEach(chainConfig => {
|
|
79
|
+
let chainIdStr = chainConfig.id->Int.toString
|
|
80
|
+
|
|
81
|
+
chainIds->Js.Array2.push(chainConfig.id)->ignore
|
|
82
|
+
|
|
83
|
+
let chainObj = Utils.Object.createNullObject()
|
|
84
|
+
chainObj
|
|
85
|
+
->Utils.Object.definePropertyWithValue("id", {enumerable: true, value: chainConfig.id})
|
|
86
|
+
->Utils.Object.definePropertyWithValue(
|
|
87
|
+
"startBlock",
|
|
88
|
+
{enumerable: true, value: chainConfig.startBlock},
|
|
89
|
+
)
|
|
90
|
+
->Utils.Object.definePropertyWithValue(
|
|
91
|
+
"endBlock",
|
|
92
|
+
{enumerable: true, value: chainConfig.endBlock},
|
|
93
|
+
)
|
|
94
|
+
->Utils.Object.definePropertyWithValue("name", {enumerable: true, value: chainConfig.name})
|
|
95
|
+
->Utils.Object.defineProperty(
|
|
96
|
+
"isLive",
|
|
97
|
+
{
|
|
98
|
+
enumerable: true,
|
|
99
|
+
get: () => {
|
|
100
|
+
switch globalGsManagerRef.contents {
|
|
101
|
+
| None => false
|
|
102
|
+
| Some(gsManager) =>
|
|
103
|
+
let state = gsManager->GlobalStateManager.getState
|
|
104
|
+
let chain = ChainMap.Chain.makeUnsafe(~chainId=chainConfig.id)
|
|
105
|
+
let chainFetcher = state.chainManager.chainFetchers->ChainMap.get(chain)
|
|
106
|
+
chainFetcher->ChainFetcher.isLive
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
)
|
|
111
|
+
->ignore
|
|
112
|
+
|
|
113
|
+
// Add contracts to chain object
|
|
114
|
+
chainConfig.contracts->Array.forEach(contract => {
|
|
115
|
+
let contractObj = Utils.Object.createNullObject()
|
|
116
|
+
contractObj
|
|
117
|
+
->Utils.Object.definePropertyWithValue("name", {enumerable: true, value: contract.name})
|
|
118
|
+
->Utils.Object.definePropertyWithValue("abi", {enumerable: true, value: contract.abi})
|
|
119
|
+
->Utils.Object.defineProperty(
|
|
120
|
+
"addresses",
|
|
121
|
+
{
|
|
122
|
+
enumerable: true,
|
|
123
|
+
get: () => {
|
|
124
|
+
switch globalGsManagerRef.contents {
|
|
125
|
+
| None => contract.addresses
|
|
126
|
+
| Some(gsManager) => {
|
|
127
|
+
let state = gsManager->GlobalStateManager.getState
|
|
128
|
+
let chain = ChainMap.Chain.makeUnsafe(~chainId=chainConfig.id)
|
|
129
|
+
let chainFetcher = state.chainManager.chainFetchers->ChainMap.get(chain)
|
|
130
|
+
let indexingContracts = chainFetcher.fetchState.indexingContracts
|
|
131
|
+
|
|
132
|
+
// Collect all addresses for this contract name from indexingContracts
|
|
133
|
+
let addresses = []
|
|
134
|
+
let values = indexingContracts->Js.Dict.values
|
|
135
|
+
for idx in 0 to values->Array.length - 1 {
|
|
136
|
+
let indexingContract = values->Js.Array2.unsafe_get(idx)
|
|
137
|
+
if indexingContract.contractName === contract.name {
|
|
138
|
+
addresses->Array.push(indexingContract.address)->ignore
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
addresses
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
)
|
|
147
|
+
->ignore
|
|
148
|
+
|
|
149
|
+
chainObj
|
|
150
|
+
->Utils.Object.definePropertyWithValue(contract.name, {enumerable: true, value: contractObj})
|
|
151
|
+
->ignore
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
// Primary key is chain ID as string
|
|
155
|
+
chains
|
|
156
|
+
->Utils.Object.definePropertyWithValue(chainIdStr, {enumerable: true, value: chainObj})
|
|
157
|
+
->ignore
|
|
158
|
+
|
|
159
|
+
// If chain has a name different from ID, add non-enumerable alias
|
|
160
|
+
if chainConfig.name !== chainIdStr {
|
|
161
|
+
chains
|
|
162
|
+
->Utils.Object.definePropertyWithValue(chainConfig.name, {enumerable: false, value: chainObj})
|
|
163
|
+
->ignore
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
indexer
|
|
167
|
+
->Utils.Object.definePropertyWithValue("chainIds", {enumerable: true, value: chainIds})
|
|
168
|
+
->ignore
|
|
169
|
+
indexer->Utils.Object.definePropertyWithValue("chains", {enumerable: true, value: chains})->ignore
|
|
170
|
+
|
|
171
|
+
indexer->Utils.magic
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
let startServer = (~getState, ~ctx: Ctx.t, ~isDevelopmentMode: bool) => {
|
|
175
|
+
open Express
|
|
176
|
+
|
|
177
|
+
let app = make()
|
|
178
|
+
|
|
179
|
+
let consoleCorsMiddleware = (req, res, next) => {
|
|
180
|
+
switch req.headers->Js.Dict.get("origin") {
|
|
181
|
+
| Some(origin) if origin === Env.prodEnvioAppUrl || origin === Env.envioAppUrl =>
|
|
182
|
+
res->setHeader("Access-Control-Allow-Origin", origin)
|
|
183
|
+
| _ => ()
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
res->setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
|
187
|
+
res->setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
|
|
188
|
+
|
|
189
|
+
if req.method === Rest.Options {
|
|
190
|
+
res->sendStatus(200)
|
|
191
|
+
} else {
|
|
192
|
+
next()
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
app->useFor("/console", consoleCorsMiddleware)
|
|
196
|
+
app->useFor("/metrics", consoleCorsMiddleware)
|
|
197
|
+
|
|
198
|
+
app->get("/healthz", (_req, res) => {
|
|
199
|
+
// this is the machine readable port used in kubernetes to check the health of this service.
|
|
200
|
+
// aditional health information could be added in the future (info about errors, back-offs, etc).
|
|
201
|
+
res->sendStatus(200)
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
app->get("/console/state", (_req, res) => {
|
|
205
|
+
let state = if isDevelopmentMode {
|
|
206
|
+
getState()
|
|
207
|
+
} else {
|
|
208
|
+
Disabled({})
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
res->json(state->S.reverseConvertToJsonOrThrow(stateSchema))
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
app->post("/console/syncCache", (_req, res) => {
|
|
215
|
+
if isDevelopmentMode {
|
|
216
|
+
(ctx.persistence->Persistence.getInitializedStorageOrThrow).dumpEffectCache()
|
|
217
|
+
->Promise.thenResolve(_ => res->json(Boolean(true)))
|
|
218
|
+
->Promise.done
|
|
219
|
+
} else {
|
|
220
|
+
res->json(Boolean(false))
|
|
221
|
+
}
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
PromClient.collectDefaultMetrics()
|
|
225
|
+
|
|
226
|
+
app->get("/metrics", (_req, res) => {
|
|
227
|
+
res->set("Content-Type", PromClient.defaultRegister->PromClient.getContentType)
|
|
228
|
+
let _ =
|
|
229
|
+
PromClient.defaultRegister
|
|
230
|
+
->PromClient.metrics
|
|
231
|
+
->Promise.thenResolve(metrics => res->endWithData(metrics))
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
let _ = app->listen(Env.serverPort)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
type args = {@as("tui-off") tuiOff?: bool}
|
|
238
|
+
|
|
239
|
+
type process
|
|
240
|
+
@val external process: process = "process"
|
|
241
|
+
@get external argv: process => 'a = "argv"
|
|
242
|
+
|
|
243
|
+
type mainArgs = Yargs.parsedArgs<args>
|
|
244
|
+
|
|
245
|
+
let start = async (
|
|
246
|
+
~makeGeneratedConfig: unit => Config.t,
|
|
247
|
+
~persistence: Persistence.t,
|
|
248
|
+
~isTest=false,
|
|
249
|
+
) => {
|
|
250
|
+
let mainArgs: mainArgs = process->argv->Yargs.hideBin->Yargs.yargs->Yargs.argv
|
|
251
|
+
let shouldUseTui = !isTest && !(mainArgs.tuiOff->Belt.Option.getWithDefault(Env.tuiOffEnvVar))
|
|
252
|
+
// The most simple check to verify whether we are running in development mode
|
|
253
|
+
// and prevent exposing the console to public, when creating a real deployment.
|
|
254
|
+
// Note: isTest overrides isDevelopmentMode to ensure proper process exit in test mode.
|
|
255
|
+
let isDevelopmentMode = !isTest && Env.Db.password === "testing"
|
|
256
|
+
|
|
257
|
+
// Register all handlers first, then get the config with registrations
|
|
258
|
+
let configWithoutRegistrations = makeGeneratedConfig()
|
|
259
|
+
let registrations = await HandlerLoader.registerAllHandlers(~config=configWithoutRegistrations)
|
|
260
|
+
let config = makeGeneratedConfig()
|
|
261
|
+
let config = if isTest {
|
|
262
|
+
{...config, shouldRollbackOnReorg: false}
|
|
263
|
+
} else {
|
|
264
|
+
config
|
|
265
|
+
}
|
|
266
|
+
let ctx = {
|
|
267
|
+
Ctx.registrations,
|
|
268
|
+
config,
|
|
269
|
+
persistence,
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
let envioVersion = Utils.EnvioPackage.value.version
|
|
273
|
+
Prometheus.Info.set(~version=envioVersion)
|
|
274
|
+
Prometheus.RollbackEnabled.set(~enabled=ctx.config.shouldRollbackOnReorg)
|
|
275
|
+
|
|
276
|
+
if !isTest {
|
|
277
|
+
startServer(~ctx, ~isDevelopmentMode, ~getState=() =>
|
|
278
|
+
switch globalGsManagerRef.contents {
|
|
279
|
+
| None => Initializing({})
|
|
280
|
+
| Some(gsManager) => {
|
|
281
|
+
let state = gsManager->GlobalStateManager.getState
|
|
282
|
+
let chains =
|
|
283
|
+
state.chainManager.chainFetchers
|
|
284
|
+
->ChainMap.values
|
|
285
|
+
->Array.map(cf => {
|
|
286
|
+
let {fetchState} = cf
|
|
287
|
+
let latestFetchedBlockNumber = Pervasives.max(
|
|
288
|
+
FetchState.bufferBlockNumber(fetchState),
|
|
289
|
+
0,
|
|
290
|
+
)
|
|
291
|
+
let knownHeight =
|
|
292
|
+
cf->ChainFetcher.hasProcessedToEndblock
|
|
293
|
+
? cf.fetchState.endBlock->Option.getWithDefault(cf.fetchState.knownHeight)
|
|
294
|
+
: cf.fetchState.knownHeight
|
|
295
|
+
|
|
296
|
+
{
|
|
297
|
+
chainId: cf.chainConfig.id->Js.Int.toFloat,
|
|
298
|
+
poweredByHyperSync: (
|
|
299
|
+
cf.sourceManager->SourceManager.getActiveSource
|
|
300
|
+
).poweredByHyperSync,
|
|
301
|
+
latestFetchedBlockNumber,
|
|
302
|
+
knownHeight,
|
|
303
|
+
numBatchesFetched: cf.numBatchesFetched,
|
|
304
|
+
endBlock: cf.fetchState.endBlock,
|
|
305
|
+
firstEventBlockNumber: cf.firstEventBlockNumber,
|
|
306
|
+
latestProcessedBlock: cf.committedProgressBlockNumber === -1
|
|
307
|
+
? None
|
|
308
|
+
: Some(cf.committedProgressBlockNumber),
|
|
309
|
+
timestampCaughtUpToHeadOrEndblock: cf.timestampCaughtUpToHeadOrEndblock,
|
|
310
|
+
numEventsProcessed: cf.numEventsProcessed,
|
|
311
|
+
numAddresses: cf.fetchState->FetchState.numAddresses,
|
|
312
|
+
}
|
|
313
|
+
})
|
|
314
|
+
Active({
|
|
315
|
+
envioVersion,
|
|
316
|
+
chains,
|
|
317
|
+
indexerStartTime: state.indexerStartTime,
|
|
318
|
+
isPreRegisteringDynamicContracts: false,
|
|
319
|
+
rollbackOnReorg: ctx.config.shouldRollbackOnReorg,
|
|
320
|
+
isUnorderedMultichainMode: switch ctx.config.multichain {
|
|
321
|
+
| Unordered => true
|
|
322
|
+
| Ordered => false
|
|
323
|
+
},
|
|
324
|
+
})
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
await ctx.persistence->Persistence.init(~chainConfigs=ctx.config.chainMap->ChainMap.values)
|
|
331
|
+
|
|
332
|
+
let chainManager = await ChainManager.makeFromDbState(
|
|
333
|
+
~initialState=ctx.persistence->Persistence.getInitializedState,
|
|
334
|
+
~config=ctx.config,
|
|
335
|
+
~registrations=ctx.registrations,
|
|
336
|
+
)
|
|
337
|
+
let globalState = GlobalState.make(~ctx, ~chainManager, ~isDevelopmentMode, ~shouldUseTui)
|
|
338
|
+
let gsManager = globalState->GlobalStateManager.make
|
|
339
|
+
if shouldUseTui {
|
|
340
|
+
let _rerender = Tui.start(~getState=() => gsManager->GlobalStateManager.getState)
|
|
341
|
+
}
|
|
342
|
+
globalGsManagerRef := Some(gsManager)
|
|
343
|
+
gsManager->GlobalStateManager.dispatchTask(NextQuery(CheckAllChains))
|
|
344
|
+
/*
|
|
345
|
+
NOTE:
|
|
346
|
+
This `ProcessEventBatch` dispatch shouldn't be necessary but we are adding for safety, it should immediately return doing
|
|
347
|
+
nothing since there is no events on the queues.
|
|
348
|
+
*/
|
|
349
|
+
|
|
350
|
+
gsManager->GlobalStateManager.dispatchTask(ProcessEventBatch)
|
|
351
|
+
}
|
package/src/Main.res.mjs
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Env from "./Env.res.mjs";
|
|
4
|
+
import * as Tui from "./tui/Tui.res.mjs";
|
|
5
|
+
import * as Caml from "rescript/lib/es6/caml.js";
|
|
6
|
+
import * as Utils from "./Utils.res.mjs";
|
|
7
|
+
import * as Js_dict from "rescript/lib/es6/js_dict.js";
|
|
8
|
+
import Express from "express";
|
|
9
|
+
import * as ChainMap from "./ChainMap.res.mjs";
|
|
10
|
+
import * as Belt_Array from "rescript/lib/es6/belt_Array.js";
|
|
11
|
+
import * as FetchState from "./FetchState.res.mjs";
|
|
12
|
+
import * as Prometheus from "./Prometheus.res.mjs";
|
|
13
|
+
import * as Belt_Option from "rescript/lib/es6/belt_Option.js";
|
|
14
|
+
import * as Caml_option from "rescript/lib/es6/caml_option.js";
|
|
15
|
+
import * as GlobalState from "./GlobalState.res.mjs";
|
|
16
|
+
import * as Persistence from "./Persistence.res.mjs";
|
|
17
|
+
import * as PromClient from "prom-client";
|
|
18
|
+
import Yargs from "yargs/yargs";
|
|
19
|
+
import * as ChainFetcher from "./ChainFetcher.res.mjs";
|
|
20
|
+
import * as ChainManager from "./ChainManager.res.mjs";
|
|
21
|
+
import * as HandlerLoader from "./HandlerLoader.res.mjs";
|
|
22
|
+
import * as SourceManager from "./sources/SourceManager.res.mjs";
|
|
23
|
+
import * as Helpers from "yargs/helpers";
|
|
24
|
+
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
25
|
+
import * as GlobalStateManager from "./GlobalStateManager.res.mjs";
|
|
26
|
+
|
|
27
|
+
var chainDataSchema = S$RescriptSchema.schema(function (s) {
|
|
28
|
+
return {
|
|
29
|
+
chainId: s.m(S$RescriptSchema.$$float),
|
|
30
|
+
poweredByHyperSync: s.m(S$RescriptSchema.bool),
|
|
31
|
+
firstEventBlockNumber: s.m(S$RescriptSchema.option(S$RescriptSchema.$$int)),
|
|
32
|
+
latestProcessedBlock: s.m(S$RescriptSchema.option(S$RescriptSchema.$$int)),
|
|
33
|
+
timestampCaughtUpToHeadOrEndblock: s.m(S$RescriptSchema.option(S$RescriptSchema.datetime(S$RescriptSchema.string, undefined))),
|
|
34
|
+
numEventsProcessed: s.m(S$RescriptSchema.$$int),
|
|
35
|
+
latestFetchedBlockNumber: s.m(S$RescriptSchema.$$int),
|
|
36
|
+
currentBlockHeight: s.m(S$RescriptSchema.$$int),
|
|
37
|
+
numBatchesFetched: s.m(S$RescriptSchema.$$int),
|
|
38
|
+
endBlock: s.m(S$RescriptSchema.option(S$RescriptSchema.$$int)),
|
|
39
|
+
numAddresses: s.m(S$RescriptSchema.$$int)
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
var stateSchema = S$RescriptSchema.union([
|
|
44
|
+
S$RescriptSchema.literal({
|
|
45
|
+
status: "disabled"
|
|
46
|
+
}),
|
|
47
|
+
S$RescriptSchema.literal({
|
|
48
|
+
status: "initializing"
|
|
49
|
+
}),
|
|
50
|
+
S$RescriptSchema.schema(function (s) {
|
|
51
|
+
return {
|
|
52
|
+
status: "active",
|
|
53
|
+
envioVersion: s.m(S$RescriptSchema.string),
|
|
54
|
+
chains: s.m(S$RescriptSchema.array(chainDataSchema)),
|
|
55
|
+
indexerStartTime: s.m(S$RescriptSchema.datetime(S$RescriptSchema.string, undefined)),
|
|
56
|
+
isPreRegisteringDynamicContracts: false,
|
|
57
|
+
isUnorderedMultichainMode: s.m(S$RescriptSchema.bool),
|
|
58
|
+
rollbackOnReorg: s.m(S$RescriptSchema.bool)
|
|
59
|
+
};
|
|
60
|
+
})
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
var globalGsManagerRef = {
|
|
64
|
+
contents: undefined
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
function getGlobalIndexer(config) {
|
|
68
|
+
var indexer = Object.create(null);
|
|
69
|
+
Object.defineProperty(Object.defineProperty(indexer, "name", {
|
|
70
|
+
enumerable: true,
|
|
71
|
+
value: config.name
|
|
72
|
+
}), "description", {
|
|
73
|
+
enumerable: true,
|
|
74
|
+
value: config.description
|
|
75
|
+
});
|
|
76
|
+
var chainIds = [];
|
|
77
|
+
var chains = Object.create(null);
|
|
78
|
+
Belt_Array.forEach(ChainMap.values(config.chainMap), (function (chainConfig) {
|
|
79
|
+
var chainIdStr = String(chainConfig.id);
|
|
80
|
+
chainIds.push(chainConfig.id);
|
|
81
|
+
var chainObj = Object.create(null);
|
|
82
|
+
Object.defineProperty(Object.defineProperty(Object.defineProperty(Object.defineProperty(Object.defineProperty(chainObj, "id", {
|
|
83
|
+
enumerable: true,
|
|
84
|
+
value: chainConfig.id
|
|
85
|
+
}), "startBlock", {
|
|
86
|
+
enumerable: true,
|
|
87
|
+
value: chainConfig.startBlock
|
|
88
|
+
}), "endBlock", {
|
|
89
|
+
enumerable: true,
|
|
90
|
+
value: chainConfig.endBlock
|
|
91
|
+
}), "name", {
|
|
92
|
+
enumerable: true,
|
|
93
|
+
value: chainConfig.name
|
|
94
|
+
}), "isLive", {
|
|
95
|
+
enumerable: true,
|
|
96
|
+
get: (function () {
|
|
97
|
+
var gsManager = globalGsManagerRef.contents;
|
|
98
|
+
if (gsManager === undefined) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
var state = GlobalStateManager.getState(Caml_option.valFromOption(gsManager));
|
|
102
|
+
var chain = ChainMap.Chain.makeUnsafe(chainConfig.id);
|
|
103
|
+
var chainFetcher = ChainMap.get(state.chainManager.chainFetchers, chain);
|
|
104
|
+
return ChainFetcher.isLive(chainFetcher);
|
|
105
|
+
})
|
|
106
|
+
});
|
|
107
|
+
Belt_Array.forEach(chainConfig.contracts, (function (contract) {
|
|
108
|
+
var contractObj = Object.create(null);
|
|
109
|
+
Object.defineProperty(Object.defineProperty(Object.defineProperty(contractObj, "name", {
|
|
110
|
+
enumerable: true,
|
|
111
|
+
value: contract.name
|
|
112
|
+
}), "abi", {
|
|
113
|
+
enumerable: true,
|
|
114
|
+
value: contract.abi
|
|
115
|
+
}), "addresses", {
|
|
116
|
+
enumerable: true,
|
|
117
|
+
get: (function () {
|
|
118
|
+
var gsManager = globalGsManagerRef.contents;
|
|
119
|
+
if (gsManager === undefined) {
|
|
120
|
+
return contract.addresses;
|
|
121
|
+
}
|
|
122
|
+
var state = GlobalStateManager.getState(Caml_option.valFromOption(gsManager));
|
|
123
|
+
var chain = ChainMap.Chain.makeUnsafe(chainConfig.id);
|
|
124
|
+
var chainFetcher = ChainMap.get(state.chainManager.chainFetchers, chain);
|
|
125
|
+
var indexingContracts = chainFetcher.fetchState.indexingContracts;
|
|
126
|
+
var addresses = [];
|
|
127
|
+
var values = Js_dict.values(indexingContracts);
|
|
128
|
+
for(var idx = 0 ,idx_finish = values.length; idx < idx_finish; ++idx){
|
|
129
|
+
var indexingContract = values[idx];
|
|
130
|
+
if (indexingContract.contractName === contract.name) {
|
|
131
|
+
addresses.push(indexingContract.address);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
}
|
|
135
|
+
return addresses;
|
|
136
|
+
})
|
|
137
|
+
});
|
|
138
|
+
Object.defineProperty(chainObj, contract.name, {
|
|
139
|
+
enumerable: true,
|
|
140
|
+
value: contractObj
|
|
141
|
+
});
|
|
142
|
+
}));
|
|
143
|
+
Object.defineProperty(chains, chainIdStr, {
|
|
144
|
+
enumerable: true,
|
|
145
|
+
value: chainObj
|
|
146
|
+
});
|
|
147
|
+
if (chainConfig.name !== chainIdStr) {
|
|
148
|
+
Object.defineProperty(chains, chainConfig.name, {
|
|
149
|
+
enumerable: false,
|
|
150
|
+
value: chainObj
|
|
151
|
+
});
|
|
152
|
+
return ;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
}));
|
|
156
|
+
Object.defineProperty(indexer, "chainIds", {
|
|
157
|
+
enumerable: true,
|
|
158
|
+
value: chainIds
|
|
159
|
+
});
|
|
160
|
+
Object.defineProperty(indexer, "chains", {
|
|
161
|
+
enumerable: true,
|
|
162
|
+
value: chains
|
|
163
|
+
});
|
|
164
|
+
return indexer;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function startServer(getState, ctx, isDevelopmentMode) {
|
|
168
|
+
var app = Express();
|
|
169
|
+
var consoleCorsMiddleware = function (req, res, next) {
|
|
170
|
+
var origin = Js_dict.get(req.headers, "origin");
|
|
171
|
+
if (origin !== undefined && (origin === Env.prodEnvioAppUrl || origin === Env.envioAppUrl)) {
|
|
172
|
+
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
173
|
+
}
|
|
174
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
|
175
|
+
res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
|
|
176
|
+
if (req.method === "OPTIONS") {
|
|
177
|
+
res.sendStatus(200);
|
|
178
|
+
return ;
|
|
179
|
+
} else {
|
|
180
|
+
return next();
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
app.use("/console", consoleCorsMiddleware);
|
|
184
|
+
app.use("/metrics", consoleCorsMiddleware);
|
|
185
|
+
app.get("/healthz", (function (_req, res) {
|
|
186
|
+
res.sendStatus(200);
|
|
187
|
+
}));
|
|
188
|
+
app.get("/console/state", (function (_req, res) {
|
|
189
|
+
var state = isDevelopmentMode ? getState() : ({
|
|
190
|
+
status: "disabled"
|
|
191
|
+
});
|
|
192
|
+
res.json(S$RescriptSchema.reverseConvertToJsonOrThrow(state, stateSchema));
|
|
193
|
+
}));
|
|
194
|
+
app.post("/console/syncCache", (function (_req, res) {
|
|
195
|
+
if (isDevelopmentMode) {
|
|
196
|
+
Persistence.getInitializedStorageOrThrow(ctx.persistence).dumpEffectCache().then(function () {
|
|
197
|
+
res.json(true);
|
|
198
|
+
});
|
|
199
|
+
} else {
|
|
200
|
+
res.json(false);
|
|
201
|
+
}
|
|
202
|
+
}));
|
|
203
|
+
PromClient.collectDefaultMetrics();
|
|
204
|
+
app.get("/metrics", (function (_req, res) {
|
|
205
|
+
res.set("Content-Type", PromClient.register.contentType);
|
|
206
|
+
PromClient.register.metrics().then(function (metrics) {
|
|
207
|
+
return res.end(metrics);
|
|
208
|
+
});
|
|
209
|
+
}));
|
|
210
|
+
app.listen(Env.serverPort);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async function start(makeGeneratedConfig, persistence, isTestOpt) {
|
|
214
|
+
var isTest = isTestOpt !== undefined ? isTestOpt : false;
|
|
215
|
+
var mainArgs = Yargs(Helpers.hideBin(process.argv)).argv;
|
|
216
|
+
var shouldUseTui = !isTest && !Belt_Option.getWithDefault(mainArgs["tui-off"], Env.tuiOffEnvVar);
|
|
217
|
+
var isDevelopmentMode = !isTest && Env.Db.password === "testing";
|
|
218
|
+
var configWithoutRegistrations = makeGeneratedConfig();
|
|
219
|
+
var registrations = await HandlerLoader.registerAllHandlers(configWithoutRegistrations);
|
|
220
|
+
var config = makeGeneratedConfig();
|
|
221
|
+
var config$1 = isTest ? ({
|
|
222
|
+
name: config.name,
|
|
223
|
+
description: config.description,
|
|
224
|
+
handlers: config.handlers,
|
|
225
|
+
contractHandlers: config.contractHandlers,
|
|
226
|
+
shouldRollbackOnReorg: false,
|
|
227
|
+
shouldSaveFullHistory: config.shouldSaveFullHistory,
|
|
228
|
+
multichain: config.multichain,
|
|
229
|
+
chainMap: config.chainMap,
|
|
230
|
+
defaultChain: config.defaultChain,
|
|
231
|
+
ecosystem: config.ecosystem,
|
|
232
|
+
enableRawEvents: config.enableRawEvents,
|
|
233
|
+
maxAddrInPartition: config.maxAddrInPartition,
|
|
234
|
+
batchSize: config.batchSize,
|
|
235
|
+
lowercaseAddresses: config.lowercaseAddresses,
|
|
236
|
+
addContractNameToContractNameMapping: config.addContractNameToContractNameMapping,
|
|
237
|
+
userEntitiesByName: config.userEntitiesByName
|
|
238
|
+
}) : config;
|
|
239
|
+
var ctx = {
|
|
240
|
+
registrations: registrations,
|
|
241
|
+
config: config$1,
|
|
242
|
+
persistence: persistence
|
|
243
|
+
};
|
|
244
|
+
var envioVersion = Utils.EnvioPackage.value.version;
|
|
245
|
+
Prometheus.Info.set(envioVersion);
|
|
246
|
+
Prometheus.RollbackEnabled.set(config$1.shouldRollbackOnReorg);
|
|
247
|
+
if (!isTest) {
|
|
248
|
+
startServer((function () {
|
|
249
|
+
var gsManager = globalGsManagerRef.contents;
|
|
250
|
+
if (gsManager === undefined) {
|
|
251
|
+
return {
|
|
252
|
+
status: "initializing"
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
var state = GlobalStateManager.getState(Caml_option.valFromOption(gsManager));
|
|
256
|
+
var chains = Belt_Array.map(ChainMap.values(state.chainManager.chainFetchers), (function (cf) {
|
|
257
|
+
var latestFetchedBlockNumber = Caml.int_max(FetchState.bufferBlockNumber(cf.fetchState), 0);
|
|
258
|
+
var knownHeight = ChainFetcher.hasProcessedToEndblock(cf) ? Belt_Option.getWithDefault(cf.fetchState.endBlock, cf.fetchState.knownHeight) : cf.fetchState.knownHeight;
|
|
259
|
+
return {
|
|
260
|
+
chainId: cf.chainConfig.id,
|
|
261
|
+
poweredByHyperSync: SourceManager.getActiveSource(cf.sourceManager).poweredByHyperSync,
|
|
262
|
+
firstEventBlockNumber: cf.firstEventBlockNumber,
|
|
263
|
+
latestProcessedBlock: cf.committedProgressBlockNumber === -1 ? undefined : cf.committedProgressBlockNumber,
|
|
264
|
+
timestampCaughtUpToHeadOrEndblock: cf.timestampCaughtUpToHeadOrEndblock,
|
|
265
|
+
numEventsProcessed: cf.numEventsProcessed,
|
|
266
|
+
latestFetchedBlockNumber: latestFetchedBlockNumber,
|
|
267
|
+
currentBlockHeight: knownHeight,
|
|
268
|
+
numBatchesFetched: cf.numBatchesFetched,
|
|
269
|
+
endBlock: cf.fetchState.endBlock,
|
|
270
|
+
numAddresses: FetchState.numAddresses(cf.fetchState)
|
|
271
|
+
};
|
|
272
|
+
}));
|
|
273
|
+
var match = config$1.multichain;
|
|
274
|
+
var tmp;
|
|
275
|
+
tmp = match === "ordered" ? false : true;
|
|
276
|
+
return {
|
|
277
|
+
status: "active",
|
|
278
|
+
envioVersion: envioVersion,
|
|
279
|
+
chains: chains,
|
|
280
|
+
indexerStartTime: state.indexerStartTime,
|
|
281
|
+
isPreRegisteringDynamicContracts: false,
|
|
282
|
+
isUnorderedMultichainMode: tmp,
|
|
283
|
+
rollbackOnReorg: config$1.shouldRollbackOnReorg
|
|
284
|
+
};
|
|
285
|
+
}), ctx, isDevelopmentMode);
|
|
286
|
+
}
|
|
287
|
+
await Persistence.init(persistence, ChainMap.values(config$1.chainMap), undefined);
|
|
288
|
+
var chainManager = await ChainManager.makeFromDbState(Persistence.getInitializedState(persistence), config$1, registrations);
|
|
289
|
+
var globalState = GlobalState.make(ctx, chainManager, isDevelopmentMode, shouldUseTui);
|
|
290
|
+
var gsManager = GlobalStateManager.make(globalState, undefined);
|
|
291
|
+
if (shouldUseTui) {
|
|
292
|
+
Tui.start(function () {
|
|
293
|
+
return GlobalStateManager.getState(gsManager);
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
globalGsManagerRef.contents = Caml_option.some(gsManager);
|
|
297
|
+
GlobalStateManager.dispatchTask(gsManager, {
|
|
298
|
+
TAG: "NextQuery",
|
|
299
|
+
_0: "CheckAllChains"
|
|
300
|
+
});
|
|
301
|
+
return GlobalStateManager.dispatchTask(gsManager, "ProcessEventBatch");
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export {
|
|
305
|
+
chainDataSchema ,
|
|
306
|
+
stateSchema ,
|
|
307
|
+
globalGsManagerRef ,
|
|
308
|
+
getGlobalIndexer ,
|
|
309
|
+
startServer ,
|
|
310
|
+
start ,
|
|
311
|
+
}
|
|
312
|
+
/* chainDataSchema Not a pure module */
|
package/src/Persistence.res
CHANGED
|
@@ -23,6 +23,7 @@ type initialChainState = {
|
|
|
23
23
|
firstEventBlockNumber: option<int>,
|
|
24
24
|
timestampCaughtUpToHeadOrEndblock: option<Js.Date.t>,
|
|
25
25
|
dynamicContracts: array<Internal.indexingContract>,
|
|
26
|
+
sourceBlockNumber: int,
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
type initialState = {
|
|
@@ -90,8 +91,6 @@ type storage = {
|
|
|
90
91
|
dumpEffectCache: unit => promise<unit>,
|
|
91
92
|
// Execute raw SQL query
|
|
92
93
|
executeUnsafe: string => promise<unknown>,
|
|
93
|
-
// Check if entity history has rows
|
|
94
|
-
hasEntityHistoryRows: unit => promise<bool>,
|
|
95
94
|
// Update chain metadata
|
|
96
95
|
setChainMeta: dict<InternalTable.Chains.metaFields> => promise<unknown>,
|
|
97
96
|
// Prune old checkpoints
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* TypeScript file generated from PgStorage.res by genType. */
|
|
2
|
+
|
|
3
|
+
/* eslint-disable */
|
|
4
|
+
/* tslint:disable */
|
|
5
|
+
|
|
6
|
+
import * as PgStorageJS from './PgStorage.res.mjs';
|
|
7
|
+
|
|
8
|
+
import type {sql as Postgres_sql} from '../src/bindings/Postgres.gen.js';
|
|
9
|
+
|
|
10
|
+
export const makeClient: () => Postgres_sql = PgStorageJS.makeClient as any;
|