envio 3.0.0-alpha.3 → 3.0.0-alpha.5
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 +0 -1
- package/index.d.ts +333 -2
- package/index.js +4 -0
- package/package.json +13 -6
- package/rescript.json +4 -1
- package/src/ChainFetcher.res +25 -1
- package/src/ChainFetcher.res.mjs +19 -1
- package/src/Config.res +212 -19
- package/src/Config.res.mjs +228 -29
- package/src/{Indexer.res → Ctx.res} +1 -1
- package/src/Ecosystem.res +2 -2
- package/src/Ecosystem.res.mjs +1 -1
- package/src/Envio.gen.ts +1 -1
- package/src/Envio.res +1 -1
- package/src/EventProcessing.res +18 -18
- package/src/EventProcessing.res.mjs +14 -14
- package/src/GlobalState.res +29 -35
- package/src/GlobalState.res.mjs +47 -47
- package/src/GlobalStateManager.res +68 -0
- package/src/GlobalStateManager.res.mjs +75 -0
- package/src/GlobalStateManager.resi +7 -0
- package/src/Internal.res +41 -1
- package/src/LogSelection.res +33 -27
- package/src/LogSelection.res.mjs +6 -0
- package/src/Main.res +342 -0
- package/src/Main.res.mjs +289 -0
- package/src/PgStorage.gen.ts +10 -0
- package/src/PgStorage.res +24 -2
- package/src/PgStorage.res.d.mts +5 -0
- package/src/PgStorage.res.mjs +22 -1
- package/src/Types.ts +1 -1
- package/src/UserContext.res +0 -1
- package/src/UserContext.res.mjs +0 -2
- 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/Ethers.res +27 -67
- package/src/bindings/Ethers.res.mjs +18 -70
- 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/Yargs.res +8 -0
- package/src/bindings/Yargs.res.mjs +2 -0
- package/src/sources/FuelSDK.res +4 -3
- package/src/sources/HyperSyncHeightStream.res +28 -110
- package/src/sources/HyperSyncHeightStream.res.mjs +30 -63
- package/src/sources/HyperSyncSource.res +11 -13
- package/src/sources/HyperSyncSource.res.mjs +20 -20
- package/src/sources/Rpc.res +43 -0
- package/src/sources/Rpc.res.mjs +31 -0
- package/src/sources/RpcSource.res +9 -4
- package/src/sources/RpcSource.res.mjs +9 -4
- package/src/sources/Source.res +1 -0
- package/src/sources/SourceManager.res +164 -81
- package/src/sources/SourceManager.res.mjs +146 -83
- package/src/sources/{Solana.res → Svm.res} +4 -4
- package/src/sources/{Solana.res.mjs → Svm.res.mjs} +4 -4
- 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 +114 -0
- package/src/tui/components/CustomHooks.res.mjs +162 -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/bindings/Ethers.gen.ts +0 -14
- /package/src/{Indexer.res.mjs → Ctx.res.mjs} +0 -0
package/src/Main.res
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
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
|
+
~registerAllHandlers: unit => promise<EventRegister.registrations>,
|
|
247
|
+
~makeGeneratedConfig: unit => Config.t,
|
|
248
|
+
~persistence: Persistence.t,
|
|
249
|
+
) => {
|
|
250
|
+
let mainArgs: mainArgs = process->argv->Yargs.hideBin->Yargs.yargs->Yargs.argv
|
|
251
|
+
let shouldUseTui = !(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
|
+
let isDevelopmentMode = Env.Db.password === "testing"
|
|
255
|
+
|
|
256
|
+
let registrations = await registerAllHandlers()
|
|
257
|
+
let config = makeGeneratedConfig()
|
|
258
|
+
let ctx = {
|
|
259
|
+
Ctx.registrations,
|
|
260
|
+
config,
|
|
261
|
+
persistence,
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
let envioVersion = Utils.EnvioPackage.value.version
|
|
265
|
+
Prometheus.Info.set(~version=envioVersion)
|
|
266
|
+
Prometheus.RollbackEnabled.set(~enabled=ctx.config.shouldRollbackOnReorg)
|
|
267
|
+
|
|
268
|
+
startServer(~ctx, ~isDevelopmentMode, ~getState=() =>
|
|
269
|
+
switch globalGsManagerRef.contents {
|
|
270
|
+
| None => Initializing({})
|
|
271
|
+
| Some(gsManager) => {
|
|
272
|
+
let state = gsManager->GlobalStateManager.getState
|
|
273
|
+
let chains =
|
|
274
|
+
state.chainManager.chainFetchers
|
|
275
|
+
->ChainMap.values
|
|
276
|
+
->Array.map(cf => {
|
|
277
|
+
let {fetchState} = cf
|
|
278
|
+
let latestFetchedBlockNumber = Pervasives.max(
|
|
279
|
+
FetchState.bufferBlockNumber(fetchState),
|
|
280
|
+
0,
|
|
281
|
+
)
|
|
282
|
+
let knownHeight =
|
|
283
|
+
cf->ChainFetcher.hasProcessedToEndblock
|
|
284
|
+
? cf.fetchState.endBlock->Option.getWithDefault(cf.fetchState.knownHeight)
|
|
285
|
+
: cf.fetchState.knownHeight
|
|
286
|
+
|
|
287
|
+
{
|
|
288
|
+
chainId: cf.chainConfig.id->Js.Int.toFloat,
|
|
289
|
+
poweredByHyperSync: (
|
|
290
|
+
cf.sourceManager->SourceManager.getActiveSource
|
|
291
|
+
).poweredByHyperSync,
|
|
292
|
+
latestFetchedBlockNumber,
|
|
293
|
+
knownHeight,
|
|
294
|
+
numBatchesFetched: cf.numBatchesFetched,
|
|
295
|
+
endBlock: cf.fetchState.endBlock,
|
|
296
|
+
firstEventBlockNumber: cf.firstEventBlockNumber,
|
|
297
|
+
latestProcessedBlock: cf.committedProgressBlockNumber === -1
|
|
298
|
+
? None
|
|
299
|
+
: Some(cf.committedProgressBlockNumber),
|
|
300
|
+
timestampCaughtUpToHeadOrEndblock: cf.timestampCaughtUpToHeadOrEndblock,
|
|
301
|
+
numEventsProcessed: cf.numEventsProcessed,
|
|
302
|
+
numAddresses: cf.fetchState->FetchState.numAddresses,
|
|
303
|
+
}
|
|
304
|
+
})
|
|
305
|
+
Active({
|
|
306
|
+
envioVersion,
|
|
307
|
+
chains,
|
|
308
|
+
indexerStartTime: state.indexerStartTime,
|
|
309
|
+
isPreRegisteringDynamicContracts: false,
|
|
310
|
+
rollbackOnReorg: ctx.config.shouldRollbackOnReorg,
|
|
311
|
+
isUnorderedMultichainMode: switch ctx.config.multichain {
|
|
312
|
+
| Unordered => true
|
|
313
|
+
| Ordered => false
|
|
314
|
+
},
|
|
315
|
+
})
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
await ctx.persistence->Persistence.init(~chainConfigs=ctx.config.chainMap->ChainMap.values)
|
|
321
|
+
|
|
322
|
+
let chainManager = await ChainManager.makeFromDbState(
|
|
323
|
+
~initialState=ctx.persistence->Persistence.getInitializedState,
|
|
324
|
+
~config=ctx.config,
|
|
325
|
+
~registrations=ctx.registrations,
|
|
326
|
+
~persistence=ctx.persistence,
|
|
327
|
+
)
|
|
328
|
+
let globalState = GlobalState.make(~ctx, ~chainManager, ~isDevelopmentMode, ~shouldUseTui)
|
|
329
|
+
let gsManager = globalState->GlobalStateManager.make
|
|
330
|
+
if shouldUseTui {
|
|
331
|
+
let _rerender = Tui.start(~getState=() => gsManager->GlobalStateManager.getState)
|
|
332
|
+
}
|
|
333
|
+
globalGsManagerRef := Some(gsManager)
|
|
334
|
+
gsManager->GlobalStateManager.dispatchTask(NextQuery(CheckAllChains))
|
|
335
|
+
/*
|
|
336
|
+
NOTE:
|
|
337
|
+
This `ProcessEventBatch` dispatch shouldn't be necessary but we are adding for safety, it should immediately return doing
|
|
338
|
+
nothing since there is no events on the queues.
|
|
339
|
+
*/
|
|
340
|
+
|
|
341
|
+
gsManager->GlobalStateManager.dispatchTask(ProcessEventBatch)
|
|
342
|
+
}
|
package/src/Main.res.mjs
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
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 SourceManager from "./sources/SourceManager.res.mjs";
|
|
22
|
+
import * as Helpers from "yargs/helpers";
|
|
23
|
+
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
24
|
+
import * as GlobalStateManager from "./GlobalStateManager.res.mjs";
|
|
25
|
+
|
|
26
|
+
var chainDataSchema = S$RescriptSchema.schema(function (s) {
|
|
27
|
+
return {
|
|
28
|
+
chainId: s.m(S$RescriptSchema.$$float),
|
|
29
|
+
poweredByHyperSync: s.m(S$RescriptSchema.bool),
|
|
30
|
+
firstEventBlockNumber: s.m(S$RescriptSchema.option(S$RescriptSchema.$$int)),
|
|
31
|
+
latestProcessedBlock: s.m(S$RescriptSchema.option(S$RescriptSchema.$$int)),
|
|
32
|
+
timestampCaughtUpToHeadOrEndblock: s.m(S$RescriptSchema.option(S$RescriptSchema.datetime(S$RescriptSchema.string, undefined))),
|
|
33
|
+
numEventsProcessed: s.m(S$RescriptSchema.$$int),
|
|
34
|
+
latestFetchedBlockNumber: s.m(S$RescriptSchema.$$int),
|
|
35
|
+
currentBlockHeight: s.m(S$RescriptSchema.$$int),
|
|
36
|
+
numBatchesFetched: s.m(S$RescriptSchema.$$int),
|
|
37
|
+
endBlock: s.m(S$RescriptSchema.option(S$RescriptSchema.$$int)),
|
|
38
|
+
numAddresses: s.m(S$RescriptSchema.$$int)
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
var stateSchema = S$RescriptSchema.union([
|
|
43
|
+
S$RescriptSchema.literal({
|
|
44
|
+
status: "disabled"
|
|
45
|
+
}),
|
|
46
|
+
S$RescriptSchema.literal({
|
|
47
|
+
status: "initializing"
|
|
48
|
+
}),
|
|
49
|
+
S$RescriptSchema.schema(function (s) {
|
|
50
|
+
return {
|
|
51
|
+
status: "active",
|
|
52
|
+
envioVersion: s.m(S$RescriptSchema.string),
|
|
53
|
+
chains: s.m(S$RescriptSchema.array(chainDataSchema)),
|
|
54
|
+
indexerStartTime: s.m(S$RescriptSchema.datetime(S$RescriptSchema.string, undefined)),
|
|
55
|
+
isPreRegisteringDynamicContracts: false,
|
|
56
|
+
isUnorderedMultichainMode: s.m(S$RescriptSchema.bool),
|
|
57
|
+
rollbackOnReorg: s.m(S$RescriptSchema.bool)
|
|
58
|
+
};
|
|
59
|
+
})
|
|
60
|
+
]);
|
|
61
|
+
|
|
62
|
+
var globalGsManagerRef = {
|
|
63
|
+
contents: undefined
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
function getGlobalIndexer(config) {
|
|
67
|
+
var indexer = Object.create(null);
|
|
68
|
+
Object.defineProperty(Object.defineProperty(indexer, "name", {
|
|
69
|
+
enumerable: true,
|
|
70
|
+
value: config.name
|
|
71
|
+
}), "description", {
|
|
72
|
+
enumerable: true,
|
|
73
|
+
value: config.description
|
|
74
|
+
});
|
|
75
|
+
var chainIds = [];
|
|
76
|
+
var chains = Object.create(null);
|
|
77
|
+
Belt_Array.forEach(ChainMap.values(config.chainMap), (function (chainConfig) {
|
|
78
|
+
var chainIdStr = String(chainConfig.id);
|
|
79
|
+
chainIds.push(chainConfig.id);
|
|
80
|
+
var chainObj = Object.create(null);
|
|
81
|
+
Object.defineProperty(Object.defineProperty(Object.defineProperty(Object.defineProperty(Object.defineProperty(chainObj, "id", {
|
|
82
|
+
enumerable: true,
|
|
83
|
+
value: chainConfig.id
|
|
84
|
+
}), "startBlock", {
|
|
85
|
+
enumerable: true,
|
|
86
|
+
value: chainConfig.startBlock
|
|
87
|
+
}), "endBlock", {
|
|
88
|
+
enumerable: true,
|
|
89
|
+
value: chainConfig.endBlock
|
|
90
|
+
}), "name", {
|
|
91
|
+
enumerable: true,
|
|
92
|
+
value: chainConfig.name
|
|
93
|
+
}), "isLive", {
|
|
94
|
+
enumerable: true,
|
|
95
|
+
get: (function () {
|
|
96
|
+
var gsManager = globalGsManagerRef.contents;
|
|
97
|
+
if (gsManager === undefined) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
var state = GlobalStateManager.getState(Caml_option.valFromOption(gsManager));
|
|
101
|
+
var chain = ChainMap.Chain.makeUnsafe(chainConfig.id);
|
|
102
|
+
var chainFetcher = ChainMap.get(state.chainManager.chainFetchers, chain);
|
|
103
|
+
return ChainFetcher.isLive(chainFetcher);
|
|
104
|
+
})
|
|
105
|
+
});
|
|
106
|
+
Belt_Array.forEach(chainConfig.contracts, (function (contract) {
|
|
107
|
+
var contractObj = Object.create(null);
|
|
108
|
+
Object.defineProperty(Object.defineProperty(Object.defineProperty(contractObj, "name", {
|
|
109
|
+
enumerable: true,
|
|
110
|
+
value: contract.name
|
|
111
|
+
}), "abi", {
|
|
112
|
+
enumerable: true,
|
|
113
|
+
value: contract.abi
|
|
114
|
+
}), "addresses", {
|
|
115
|
+
enumerable: true,
|
|
116
|
+
get: (function () {
|
|
117
|
+
var gsManager = globalGsManagerRef.contents;
|
|
118
|
+
if (gsManager === undefined) {
|
|
119
|
+
return contract.addresses;
|
|
120
|
+
}
|
|
121
|
+
var state = GlobalStateManager.getState(Caml_option.valFromOption(gsManager));
|
|
122
|
+
var chain = ChainMap.Chain.makeUnsafe(chainConfig.id);
|
|
123
|
+
var chainFetcher = ChainMap.get(state.chainManager.chainFetchers, chain);
|
|
124
|
+
var indexingContracts = chainFetcher.fetchState.indexingContracts;
|
|
125
|
+
var addresses = [];
|
|
126
|
+
var values = Js_dict.values(indexingContracts);
|
|
127
|
+
for(var idx = 0 ,idx_finish = values.length; idx < idx_finish; ++idx){
|
|
128
|
+
var indexingContract = values[idx];
|
|
129
|
+
if (indexingContract.contractName === contract.name) {
|
|
130
|
+
addresses.push(indexingContract.address);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
}
|
|
134
|
+
return addresses;
|
|
135
|
+
})
|
|
136
|
+
});
|
|
137
|
+
Object.defineProperty(chainObj, contract.name, {
|
|
138
|
+
enumerable: true,
|
|
139
|
+
value: contractObj
|
|
140
|
+
});
|
|
141
|
+
}));
|
|
142
|
+
Object.defineProperty(chains, chainIdStr, {
|
|
143
|
+
enumerable: true,
|
|
144
|
+
value: chainObj
|
|
145
|
+
});
|
|
146
|
+
if (chainConfig.name !== chainIdStr) {
|
|
147
|
+
Object.defineProperty(chains, chainConfig.name, {
|
|
148
|
+
enumerable: false,
|
|
149
|
+
value: chainObj
|
|
150
|
+
});
|
|
151
|
+
return ;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
}));
|
|
155
|
+
Object.defineProperty(indexer, "chainIds", {
|
|
156
|
+
enumerable: true,
|
|
157
|
+
value: chainIds
|
|
158
|
+
});
|
|
159
|
+
Object.defineProperty(indexer, "chains", {
|
|
160
|
+
enumerable: true,
|
|
161
|
+
value: chains
|
|
162
|
+
});
|
|
163
|
+
return indexer;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function startServer(getState, ctx, isDevelopmentMode) {
|
|
167
|
+
var app = Express();
|
|
168
|
+
var consoleCorsMiddleware = function (req, res, next) {
|
|
169
|
+
var origin = Js_dict.get(req.headers, "origin");
|
|
170
|
+
if (origin !== undefined && (origin === Env.prodEnvioAppUrl || origin === Env.envioAppUrl)) {
|
|
171
|
+
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
172
|
+
}
|
|
173
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
|
174
|
+
res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
|
|
175
|
+
if (req.method === "OPTIONS") {
|
|
176
|
+
res.sendStatus(200);
|
|
177
|
+
return ;
|
|
178
|
+
} else {
|
|
179
|
+
return next();
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
app.use("/console", consoleCorsMiddleware);
|
|
183
|
+
app.use("/metrics", consoleCorsMiddleware);
|
|
184
|
+
app.get("/healthz", (function (_req, res) {
|
|
185
|
+
res.sendStatus(200);
|
|
186
|
+
}));
|
|
187
|
+
app.get("/console/state", (function (_req, res) {
|
|
188
|
+
var state = isDevelopmentMode ? getState() : ({
|
|
189
|
+
status: "disabled"
|
|
190
|
+
});
|
|
191
|
+
res.json(S$RescriptSchema.reverseConvertToJsonOrThrow(state, stateSchema));
|
|
192
|
+
}));
|
|
193
|
+
app.post("/console/syncCache", (function (_req, res) {
|
|
194
|
+
if (isDevelopmentMode) {
|
|
195
|
+
Persistence.getInitializedStorageOrThrow(ctx.persistence).dumpEffectCache().then(function () {
|
|
196
|
+
res.json(true);
|
|
197
|
+
});
|
|
198
|
+
} else {
|
|
199
|
+
res.json(false);
|
|
200
|
+
}
|
|
201
|
+
}));
|
|
202
|
+
PromClient.collectDefaultMetrics();
|
|
203
|
+
app.get("/metrics", (function (_req, res) {
|
|
204
|
+
res.set("Content-Type", PromClient.register.contentType);
|
|
205
|
+
PromClient.register.metrics().then(function (metrics) {
|
|
206
|
+
return res.end(metrics);
|
|
207
|
+
});
|
|
208
|
+
}));
|
|
209
|
+
app.listen(Env.serverPort);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function start(registerAllHandlers, makeGeneratedConfig, persistence) {
|
|
213
|
+
var mainArgs = Yargs(Helpers.hideBin(process.argv)).argv;
|
|
214
|
+
var shouldUseTui = !Belt_Option.getWithDefault(mainArgs["tui-off"], Env.tuiOffEnvVar);
|
|
215
|
+
var isDevelopmentMode = Env.Db.password === "testing";
|
|
216
|
+
var registrations = await registerAllHandlers();
|
|
217
|
+
var config = makeGeneratedConfig();
|
|
218
|
+
var ctx = {
|
|
219
|
+
registrations: registrations,
|
|
220
|
+
config: config,
|
|
221
|
+
persistence: persistence
|
|
222
|
+
};
|
|
223
|
+
var envioVersion = Utils.EnvioPackage.value.version;
|
|
224
|
+
Prometheus.Info.set(envioVersion);
|
|
225
|
+
Prometheus.RollbackEnabled.set(config.shouldRollbackOnReorg);
|
|
226
|
+
startServer((function () {
|
|
227
|
+
var gsManager = globalGsManagerRef.contents;
|
|
228
|
+
if (gsManager === undefined) {
|
|
229
|
+
return {
|
|
230
|
+
status: "initializing"
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
var state = GlobalStateManager.getState(Caml_option.valFromOption(gsManager));
|
|
234
|
+
var chains = Belt_Array.map(ChainMap.values(state.chainManager.chainFetchers), (function (cf) {
|
|
235
|
+
var latestFetchedBlockNumber = Caml.int_max(FetchState.bufferBlockNumber(cf.fetchState), 0);
|
|
236
|
+
var knownHeight = ChainFetcher.hasProcessedToEndblock(cf) ? Belt_Option.getWithDefault(cf.fetchState.endBlock, cf.fetchState.knownHeight) : cf.fetchState.knownHeight;
|
|
237
|
+
return {
|
|
238
|
+
chainId: cf.chainConfig.id,
|
|
239
|
+
poweredByHyperSync: SourceManager.getActiveSource(cf.sourceManager).poweredByHyperSync,
|
|
240
|
+
firstEventBlockNumber: cf.firstEventBlockNumber,
|
|
241
|
+
latestProcessedBlock: cf.committedProgressBlockNumber === -1 ? undefined : cf.committedProgressBlockNumber,
|
|
242
|
+
timestampCaughtUpToHeadOrEndblock: cf.timestampCaughtUpToHeadOrEndblock,
|
|
243
|
+
numEventsProcessed: cf.numEventsProcessed,
|
|
244
|
+
latestFetchedBlockNumber: latestFetchedBlockNumber,
|
|
245
|
+
currentBlockHeight: knownHeight,
|
|
246
|
+
numBatchesFetched: cf.numBatchesFetched,
|
|
247
|
+
endBlock: cf.fetchState.endBlock,
|
|
248
|
+
numAddresses: FetchState.numAddresses(cf.fetchState)
|
|
249
|
+
};
|
|
250
|
+
}));
|
|
251
|
+
var match = config.multichain;
|
|
252
|
+
var tmp;
|
|
253
|
+
tmp = match === "ordered" ? false : true;
|
|
254
|
+
return {
|
|
255
|
+
status: "active",
|
|
256
|
+
envioVersion: envioVersion,
|
|
257
|
+
chains: chains,
|
|
258
|
+
indexerStartTime: state.indexerStartTime,
|
|
259
|
+
isPreRegisteringDynamicContracts: false,
|
|
260
|
+
isUnorderedMultichainMode: tmp,
|
|
261
|
+
rollbackOnReorg: config.shouldRollbackOnReorg
|
|
262
|
+
};
|
|
263
|
+
}), ctx, isDevelopmentMode);
|
|
264
|
+
await Persistence.init(persistence, ChainMap.values(config.chainMap), undefined);
|
|
265
|
+
var chainManager = await ChainManager.makeFromDbState(Persistence.getInitializedState(persistence), config, registrations, persistence);
|
|
266
|
+
var globalState = GlobalState.make(ctx, chainManager, isDevelopmentMode, shouldUseTui);
|
|
267
|
+
var gsManager = GlobalStateManager.make(globalState, undefined);
|
|
268
|
+
if (shouldUseTui) {
|
|
269
|
+
Tui.start(function () {
|
|
270
|
+
return GlobalStateManager.getState(gsManager);
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
globalGsManagerRef.contents = Caml_option.some(gsManager);
|
|
274
|
+
GlobalStateManager.dispatchTask(gsManager, {
|
|
275
|
+
TAG: "NextQuery",
|
|
276
|
+
_0: "CheckAllChains"
|
|
277
|
+
});
|
|
278
|
+
return GlobalStateManager.dispatchTask(gsManager, "ProcessEventBatch");
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export {
|
|
282
|
+
chainDataSchema ,
|
|
283
|
+
stateSchema ,
|
|
284
|
+
globalGsManagerRef ,
|
|
285
|
+
getGlobalIndexer ,
|
|
286
|
+
startServer ,
|
|
287
|
+
start ,
|
|
288
|
+
}
|
|
289
|
+
/* chainDataSchema Not a pure module */
|
|
@@ -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;
|
package/src/PgStorage.res
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
let getCacheRowCountFnName = "get_cache_row_count"
|
|
2
2
|
|
|
3
|
+
// Only needed for some old tests
|
|
4
|
+
// Remove @genType in the future
|
|
5
|
+
@genType
|
|
6
|
+
let makeClient = () => {
|
|
7
|
+
Postgres.makeSql(
|
|
8
|
+
~config={
|
|
9
|
+
host: Env.Db.host,
|
|
10
|
+
port: Env.Db.port,
|
|
11
|
+
username: Env.Db.user,
|
|
12
|
+
password: Env.Db.password,
|
|
13
|
+
database: Env.Db.database,
|
|
14
|
+
ssl: Env.Db.ssl,
|
|
15
|
+
// TODO: think how we want to pipe these logs to pino.
|
|
16
|
+
onnotice: ?(
|
|
17
|
+
Env.userLogLevel == #warn || Env.userLogLevel == #error ? None : Some(_str => ())
|
|
18
|
+
),
|
|
19
|
+
transform: {undefined: Null},
|
|
20
|
+
max: 2,
|
|
21
|
+
// debug: (~connection, ~query, ~params as _, ~types as _) => Js.log2(connection, query),
|
|
22
|
+
},
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
3
26
|
let makeCreateIndexQuery = (~tableName, ~indexFields, ~pgSchema) => {
|
|
4
27
|
let indexName = tableName ++ "_" ++ indexFields->Js.Array2.joinWith("_")
|
|
5
28
|
let index = indexFields->Belt.Array.map(idx => `"${idx}"`)->Js.Array2.joinWith(", ")
|
|
@@ -1056,8 +1079,7 @@ let make = (
|
|
|
1056
1079
|
}
|
|
1057
1080
|
|
|
1058
1081
|
let cacheDirPath = NodeJs.Path.resolve([
|
|
1059
|
-
// Right
|
|
1060
|
-
"..",
|
|
1082
|
+
// Right at the project root
|
|
1061
1083
|
".envio",
|
|
1062
1084
|
"cache",
|
|
1063
1085
|
])
|