envio 3.0.0-alpha.2 → 3.0.0-alpha.20
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 +164 -30
- package/bin.mjs +49 -0
- package/evm.schema.json +79 -169
- package/fuel.schema.json +50 -21
- package/index.d.ts +497 -1
- package/index.js +4 -0
- package/package.json +42 -31
- package/rescript.json +4 -1
- package/src/Batch.res +11 -8
- package/src/Batch.res.mjs +11 -9
- package/src/ChainFetcher.res +531 -0
- package/src/ChainFetcher.res.mjs +339 -0
- package/src/ChainManager.res +190 -0
- package/src/ChainManager.res.mjs +166 -0
- package/src/Change.res +3 -3
- package/src/Config.gen.ts +19 -0
- package/src/Config.res +737 -22
- package/src/Config.res.mjs +703 -26
- package/src/{Indexer.res → Ctx.res} +1 -1
- package/src/Ecosystem.res +9 -124
- package/src/Ecosystem.res.mjs +19 -160
- package/src/Env.res +30 -74
- package/src/Env.res.mjs +25 -87
- package/src/Envio.gen.ts +3 -1
- package/src/Envio.res +20 -9
- package/src/EventProcessing.res +469 -0
- package/src/EventProcessing.res.mjs +337 -0
- package/src/EvmTypes.gen.ts +6 -0
- package/src/EvmTypes.res +1 -0
- package/src/FetchState.res +1256 -639
- package/src/FetchState.res.mjs +1135 -612
- package/src/GlobalState.res +1190 -0
- package/src/GlobalState.res.mjs +1183 -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/HandlerRegister.res +357 -0
- package/src/HandlerRegister.res.mjs +299 -0
- package/src/{EventRegister.resi → HandlerRegister.resi} +13 -13
- package/src/Hasura.res +111 -175
- package/src/Hasura.res.mjs +88 -150
- package/src/InMemoryStore.res +1 -1
- package/src/InMemoryStore.res.mjs +3 -3
- package/src/InMemoryTable.res +1 -1
- package/src/InMemoryTable.res.mjs +1 -1
- package/src/Internal.gen.ts +4 -0
- package/src/Internal.res +230 -12
- package/src/Internal.res.mjs +115 -1
- 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 +21 -7
- package/src/Logging.res.mjs +16 -8
- package/src/Main.res +377 -0
- package/src/Main.res.mjs +339 -0
- package/src/Persistence.res +7 -21
- package/src/Persistence.res.mjs +3 -3
- package/src/PgStorage.gen.ts +10 -0
- package/src/PgStorage.res +116 -69
- package/src/PgStorage.res.d.mts +5 -0
- package/src/PgStorage.res.mjs +93 -50
- package/src/Prometheus.res +294 -224
- package/src/Prometheus.res.mjs +353 -340
- package/src/ReorgDetection.res +6 -10
- package/src/ReorgDetection.res.mjs +6 -6
- package/src/SafeCheckpointTracking.res +4 -4
- package/src/SafeCheckpointTracking.res.mjs +2 -2
- package/src/Sink.res +4 -2
- package/src/Sink.res.mjs +2 -1
- package/src/TableIndices.res +0 -1
- package/src/TestIndexer.res +692 -0
- package/src/TestIndexer.res.mjs +527 -0
- package/src/TestIndexerProxyStorage.res +205 -0
- package/src/TestIndexerProxyStorage.res.mjs +151 -0
- package/src/TopicFilter.res +1 -1
- package/src/Types.ts +1 -1
- package/src/UserContext.res +424 -0
- package/src/UserContext.res.mjs +279 -0
- package/src/Utils.res +97 -26
- package/src/Utils.res.mjs +91 -44
- package/src/bindings/BigInt.res +10 -0
- package/src/bindings/BigInt.res.mjs +15 -0
- package/src/bindings/ClickHouse.res +120 -23
- package/src/bindings/ClickHouse.res.mjs +118 -28
- package/src/bindings/DateFns.res +74 -0
- package/src/bindings/DateFns.res.mjs +22 -0
- package/src/bindings/EventSource.res +8 -1
- package/src/bindings/EventSource.res.mjs +8 -1
- package/src/bindings/Express.res +1 -0
- package/src/bindings/Hrtime.res +14 -1
- package/src/bindings/Hrtime.res.mjs +22 -2
- package/src/bindings/Hrtime.resi +4 -0
- package/src/bindings/Lodash.res +0 -1
- package/src/bindings/NodeJs.res +49 -3
- package/src/bindings/NodeJs.res.mjs +11 -3
- package/src/bindings/Pino.res +24 -10
- package/src/bindings/Pino.res.mjs +14 -8
- package/src/bindings/Postgres.gen.ts +8 -0
- package/src/bindings/Postgres.res +5 -1
- package/src/bindings/Postgres.res.d.mts +5 -0
- package/src/bindings/PromClient.res +0 -10
- package/src/bindings/PromClient.res.mjs +0 -3
- package/src/bindings/Vitest.res +142 -0
- package/src/bindings/Vitest.res.mjs +9 -0
- package/src/bindings/WebSocket.res +27 -0
- package/src/bindings/WebSocket.res.mjs +2 -0
- package/src/bindings/Yargs.res +8 -0
- package/src/bindings/Yargs.res.mjs +2 -0
- package/src/db/EntityHistory.res +7 -7
- package/src/db/EntityHistory.res.mjs +9 -9
- package/src/db/InternalTable.res +59 -111
- package/src/db/InternalTable.res.mjs +73 -104
- package/src/db/Table.res +27 -8
- package/src/db/Table.res.mjs +25 -14
- package/src/sources/Evm.res +84 -0
- package/src/sources/Evm.res.mjs +105 -0
- package/src/sources/EvmChain.res +94 -0
- package/src/sources/EvmChain.res.mjs +60 -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 +33 -13
- package/src/sources/HyperFuelSource.res.mjs +24 -16
- package/src/sources/HyperSync.res +36 -6
- package/src/sources/HyperSync.res.mjs +9 -7
- package/src/sources/HyperSync.resi +4 -0
- package/src/sources/HyperSyncClient.res +1 -1
- package/src/sources/HyperSyncHeightStream.res +47 -116
- package/src/sources/HyperSyncHeightStream.res.mjs +46 -73
- package/src/sources/HyperSyncSource.res +118 -139
- package/src/sources/HyperSyncSource.res.mjs +104 -121
- package/src/sources/Rpc.res +86 -14
- package/src/sources/Rpc.res.mjs +101 -9
- package/src/sources/RpcSource.res +621 -364
- package/src/sources/RpcSource.res.mjs +843 -410
- package/src/sources/RpcWebSocketHeightStream.res +181 -0
- package/src/sources/RpcWebSocketHeightStream.res.mjs +196 -0
- package/src/sources/Source.res +7 -5
- package/src/sources/SourceManager.res +325 -225
- package/src/sources/SourceManager.res.mjs +314 -171
- package/src/sources/SourceManager.resi +17 -6
- package/src/sources/Svm.res +81 -0
- package/src/sources/Svm.res.mjs +90 -0
- package/src/tui/Tui.res +247 -0
- package/src/tui/Tui.res.mjs +337 -0
- package/src/tui/bindings/Ink.res +371 -0
- package/src/tui/bindings/Ink.res.mjs +72 -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 +174 -0
- package/src/tui/components/SyncETA.res.mjs +263 -0
- package/src/tui/components/TuiData.res +47 -0
- package/src/tui/components/TuiData.res.mjs +34 -0
- package/svm.schema.json +112 -0
- package/bin.js +0 -48
- package/src/EventRegister.res +0 -241
- package/src/EventRegister.res.mjs +0 -240
- package/src/bindings/Ethers.gen.ts +0 -14
- package/src/bindings/Ethers.res +0 -204
- package/src/bindings/Ethers.res.mjs +0 -130
- /package/src/{Indexer.res.mjs → Ctx.res.mjs} +0 -0
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
type t
|
|
2
2
|
|
|
3
|
+
type sourceRole = Primary | Secondary
|
|
4
|
+
|
|
5
|
+
let getSourceRole: (
|
|
6
|
+
~sourceFor: Source.sourceFor,
|
|
7
|
+
~isLive: bool,
|
|
8
|
+
~hasLive: bool,
|
|
9
|
+
) => option<sourceRole>
|
|
10
|
+
|
|
3
11
|
let make: (
|
|
4
12
|
~sources: array<Source.t>,
|
|
5
13
|
~maxPartitionConcurrency: int,
|
|
6
|
-
~
|
|
14
|
+
~isLive: bool,
|
|
15
|
+
~newBlockStallTimeout: int=?,
|
|
16
|
+
~newBlockStallTimeoutLive: int=?,
|
|
7
17
|
~stalledPollingInterval: int=?,
|
|
18
|
+
~recoveryTimeout: float=?,
|
|
8
19
|
~getHeightRetryInterval: (~retry: int) => int=?,
|
|
9
20
|
) => t
|
|
10
21
|
|
|
@@ -13,19 +24,19 @@ let getActiveSource: t => Source.t
|
|
|
13
24
|
let fetchNext: (
|
|
14
25
|
t,
|
|
15
26
|
~fetchState: FetchState.t,
|
|
16
|
-
~currentBlockHeight: int,
|
|
17
27
|
~executeQuery: FetchState.query => promise<unit>,
|
|
18
|
-
~waitForNewBlock: (~
|
|
19
|
-
~onNewBlock: (~
|
|
28
|
+
~waitForNewBlock: (~knownHeight: int) => promise<int>,
|
|
29
|
+
~onNewBlock: (~knownHeight: int) => unit,
|
|
20
30
|
~stateId: int,
|
|
21
31
|
) => promise<unit>
|
|
22
32
|
|
|
23
|
-
let waitForNewBlock: (t, ~
|
|
33
|
+
let waitForNewBlock: (t, ~knownHeight: int, ~isLive: bool) => promise<int>
|
|
24
34
|
|
|
25
35
|
let executeQuery: (
|
|
26
36
|
t,
|
|
27
37
|
~query: FetchState.query,
|
|
28
|
-
~
|
|
38
|
+
~knownHeight: int,
|
|
39
|
+
~isLive: bool,
|
|
29
40
|
) => promise<Source.blockRangeFetchResponse>
|
|
30
41
|
|
|
31
42
|
let makeGetHeightRetryInterval: (
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
@get external getNumber: Internal.eventBlock => int = "height"
|
|
2
|
+
@get external getTimestamp: Internal.eventBlock => int = "time"
|
|
3
|
+
@get external getId: Internal.eventBlock => string = "hash"
|
|
4
|
+
|
|
5
|
+
let cleanUpRawEventFieldsInPlace: Js.Json.t => unit = %raw(`fields => {
|
|
6
|
+
delete fields.hash
|
|
7
|
+
delete fields.height
|
|
8
|
+
delete fields.time
|
|
9
|
+
}`)
|
|
10
|
+
|
|
11
|
+
let ecosystem: Ecosystem.t = {
|
|
12
|
+
name: Svm,
|
|
13
|
+
blockFields: ["slot"],
|
|
14
|
+
transactionFields: [],
|
|
15
|
+
blockNumberName: "height",
|
|
16
|
+
blockTimestampName: "time",
|
|
17
|
+
blockHashName: "hash",
|
|
18
|
+
getNumber,
|
|
19
|
+
getTimestamp,
|
|
20
|
+
getId,
|
|
21
|
+
cleanUpRawEventFieldsInPlace,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module GetFinalizedSlot = {
|
|
25
|
+
let route = Rpc.makeRpcRoute(
|
|
26
|
+
"getSlot",
|
|
27
|
+
S.tuple(s => {
|
|
28
|
+
s.tag(0, {"commitment": "finalized"})
|
|
29
|
+
()
|
|
30
|
+
}),
|
|
31
|
+
S.int,
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let makeRPCSource = (~chain, ~rpc: string): Source.t => {
|
|
36
|
+
let client = Rest.client(rpc)
|
|
37
|
+
let chainId = chain->ChainMap.Chain.toChainId
|
|
38
|
+
|
|
39
|
+
let urlHost = switch Utils.Url.getHostFromUrl(rpc) {
|
|
40
|
+
| None =>
|
|
41
|
+
Js.Exn.raiseError(
|
|
42
|
+
`The RPC url for chain ${chainId->Belt.Int.toString} is in incorrect format. The RPC url needs to start with either http:// or https://`,
|
|
43
|
+
)
|
|
44
|
+
| Some(host) => host
|
|
45
|
+
}
|
|
46
|
+
let name = `RPC (${urlHost})`
|
|
47
|
+
|
|
48
|
+
{
|
|
49
|
+
name,
|
|
50
|
+
sourceFor: Sync,
|
|
51
|
+
chain,
|
|
52
|
+
poweredByHyperSync: false,
|
|
53
|
+
pollingInterval: 10_000,
|
|
54
|
+
getBlockHashes: (~blockNumbers as _, ~logger as _) =>
|
|
55
|
+
Js.Exn.raiseError("Svm does not support getting block hashes"),
|
|
56
|
+
getHeightOrThrow: async () => {
|
|
57
|
+
let timerRef = Hrtime.makeTimer()
|
|
58
|
+
let height = await GetFinalizedSlot.route->Rest.fetch((), ~client)
|
|
59
|
+
let seconds = timerRef->Hrtime.timeSince->Hrtime.toSecondsFloat
|
|
60
|
+
Prometheus.SourceRequestCount.increment(~sourceName=name, ~chainId, ~method="getSlot")
|
|
61
|
+
Prometheus.SourceRequestCount.addSeconds(
|
|
62
|
+
~sourceName=name,
|
|
63
|
+
~chainId,
|
|
64
|
+
~method="getSlot",
|
|
65
|
+
~seconds,
|
|
66
|
+
)
|
|
67
|
+
height
|
|
68
|
+
},
|
|
69
|
+
getItemsOrThrow: (
|
|
70
|
+
~fromBlock as _,
|
|
71
|
+
~toBlock as _,
|
|
72
|
+
~addressesByContractName as _,
|
|
73
|
+
~indexingContracts as _,
|
|
74
|
+
~knownHeight as _,
|
|
75
|
+
~partitionId as _,
|
|
76
|
+
~selection as _,
|
|
77
|
+
~retry as _,
|
|
78
|
+
~logger as _,
|
|
79
|
+
) => Js.Exn.raiseError("Svm does not support getting items"),
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Rpc from "./Rpc.res.mjs";
|
|
4
|
+
import * as Rest from "../vendored/Rest.res.mjs";
|
|
5
|
+
import * as Utils from "../Utils.res.mjs";
|
|
6
|
+
import * as Hrtime from "../bindings/Hrtime.res.mjs";
|
|
7
|
+
import * as Js_exn from "rescript/lib/es6/js_exn.js";
|
|
8
|
+
import * as Prometheus from "../Prometheus.res.mjs";
|
|
9
|
+
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
10
|
+
|
|
11
|
+
var cleanUpRawEventFieldsInPlace = (fields => {
|
|
12
|
+
delete fields.hash
|
|
13
|
+
delete fields.height
|
|
14
|
+
delete fields.time
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
var ecosystem_blockFields = ["slot"];
|
|
18
|
+
|
|
19
|
+
var ecosystem_transactionFields = [];
|
|
20
|
+
|
|
21
|
+
function ecosystem_getNumber(prim) {
|
|
22
|
+
return prim.height;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function ecosystem_getTimestamp(prim) {
|
|
26
|
+
return prim.time;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function ecosystem_getId(prim) {
|
|
30
|
+
return prim.hash;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
var ecosystem = {
|
|
34
|
+
name: "svm",
|
|
35
|
+
blockFields: ecosystem_blockFields,
|
|
36
|
+
transactionFields: ecosystem_transactionFields,
|
|
37
|
+
blockNumberName: "height",
|
|
38
|
+
blockTimestampName: "time",
|
|
39
|
+
blockHashName: "hash",
|
|
40
|
+
getNumber: ecosystem_getNumber,
|
|
41
|
+
getTimestamp: ecosystem_getTimestamp,
|
|
42
|
+
getId: ecosystem_getId,
|
|
43
|
+
cleanUpRawEventFieldsInPlace: cleanUpRawEventFieldsInPlace
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
var route = Rpc.makeRpcRoute("getSlot", S$RescriptSchema.tuple(function (s) {
|
|
47
|
+
s.tag(0, {
|
|
48
|
+
commitment: "finalized"
|
|
49
|
+
});
|
|
50
|
+
}), S$RescriptSchema.$$int);
|
|
51
|
+
|
|
52
|
+
var GetFinalizedSlot = {
|
|
53
|
+
route: route
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
function makeRPCSource(chain, rpc) {
|
|
57
|
+
var client = Rest.client(rpc, undefined);
|
|
58
|
+
var host = Utils.Url.getHostFromUrl(rpc);
|
|
59
|
+
var urlHost = host !== undefined ? host : Js_exn.raiseError("The RPC url for chain " + String(chain) + " is in incorrect format. The RPC url needs to start with either http:// or https://");
|
|
60
|
+
var name = "RPC (" + urlHost + ")";
|
|
61
|
+
return {
|
|
62
|
+
name: name,
|
|
63
|
+
sourceFor: "Sync",
|
|
64
|
+
chain: chain,
|
|
65
|
+
poweredByHyperSync: false,
|
|
66
|
+
pollingInterval: 10000,
|
|
67
|
+
getBlockHashes: (function (param, param$1) {
|
|
68
|
+
return Js_exn.raiseError("Svm does not support getting block hashes");
|
|
69
|
+
}),
|
|
70
|
+
getHeightOrThrow: (async function () {
|
|
71
|
+
var timerRef = Hrtime.makeTimer();
|
|
72
|
+
var height = await Rest.$$fetch(route, undefined, client);
|
|
73
|
+
var seconds = Hrtime.toSecondsFloat(Hrtime.timeSince(timerRef));
|
|
74
|
+
Prometheus.SourceRequestCount.increment(name, chain, "getSlot");
|
|
75
|
+
Prometheus.SourceRequestCount.addSeconds(name, chain, "getSlot", seconds);
|
|
76
|
+
return height;
|
|
77
|
+
}),
|
|
78
|
+
getItemsOrThrow: (function (param, param$1, param$2, param$3, param$4, param$5, param$6, param$7, param$8) {
|
|
79
|
+
return Js_exn.raiseError("Svm does not support getting items");
|
|
80
|
+
})
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export {
|
|
85
|
+
cleanUpRawEventFieldsInPlace ,
|
|
86
|
+
ecosystem ,
|
|
87
|
+
GetFinalizedSlot ,
|
|
88
|
+
makeRPCSource ,
|
|
89
|
+
}
|
|
90
|
+
/* route Not a pure module */
|
package/src/tui/Tui.res
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
open Ink
|
|
2
|
+
open Belt
|
|
3
|
+
|
|
4
|
+
module ChainLine = {
|
|
5
|
+
@react.component
|
|
6
|
+
let make = (
|
|
7
|
+
~chainId,
|
|
8
|
+
~maxChainIdLength,
|
|
9
|
+
~stdoutColumns: int,
|
|
10
|
+
~progressBlock,
|
|
11
|
+
~bufferBlock,
|
|
12
|
+
~sourceBlock,
|
|
13
|
+
~startBlock,
|
|
14
|
+
~endBlock,
|
|
15
|
+
~poweredByHyperSync,
|
|
16
|
+
~eventsProcessed,
|
|
17
|
+
) => {
|
|
18
|
+
let chainsWidth = Pervasives.min(stdoutColumns - 2, 60)
|
|
19
|
+
let headerWidth = maxChainIdLength + 10 // 10 for additional text
|
|
20
|
+
|
|
21
|
+
switch (progressBlock, bufferBlock, sourceBlock) {
|
|
22
|
+
| (Some(progressBlock), Some(bufferBlock), Some(sourceBlock)) =>
|
|
23
|
+
let toBlock = switch endBlock {
|
|
24
|
+
| Some(endBlock) => Pervasives.min(sourceBlock, endBlock)
|
|
25
|
+
| None => sourceBlock
|
|
26
|
+
}
|
|
27
|
+
let progressBlockStr = progressBlock->TuiData.formatLocaleString
|
|
28
|
+
let toBlockStr = toBlock->TuiData.formatLocaleString
|
|
29
|
+
let eventsStr = eventsProcessed->TuiData.formatFloatLocaleString
|
|
30
|
+
|
|
31
|
+
let blocksText =
|
|
32
|
+
`Blocks: ${progressBlockStr} / ${toBlockStr}` ++
|
|
33
|
+
(endBlock->Option.isSome ? " (End Block)" : "") ++ ` `
|
|
34
|
+
let eventsText = `Events: ${eventsStr}`
|
|
35
|
+
|
|
36
|
+
let fitsSameLine = blocksText->String.length + eventsText->String.length <= chainsWidth
|
|
37
|
+
|
|
38
|
+
<Box flexDirection={Column}>
|
|
39
|
+
<Box flexDirection=Row width=Num(chainsWidth)>
|
|
40
|
+
<Box width={Num(headerWidth)}>
|
|
41
|
+
<Text> {"Chain: "->React.string} </Text>
|
|
42
|
+
<Text bold=true> {chainId->React.string} </Text>
|
|
43
|
+
<Text> {" "->React.string} </Text>
|
|
44
|
+
{poweredByHyperSync ? <Text color=Secondary> {"⚡"->React.string} </Text> : React.null}
|
|
45
|
+
</Box>
|
|
46
|
+
<BufferedProgressBar
|
|
47
|
+
barWidth={chainsWidth - headerWidth}
|
|
48
|
+
loaded={progressBlock - startBlock}
|
|
49
|
+
buffered={bufferBlock - startBlock}
|
|
50
|
+
outOf={toBlock - startBlock}
|
|
51
|
+
loadingColor={Secondary}
|
|
52
|
+
/>
|
|
53
|
+
</Box>
|
|
54
|
+
<Box flexDirection={Row}>
|
|
55
|
+
<Text color={Gray}> {blocksText->React.string} </Text>
|
|
56
|
+
{fitsSameLine ? <Text color={Gray}> {eventsText->React.string} </Text> : React.null}
|
|
57
|
+
</Box>
|
|
58
|
+
{fitsSameLine
|
|
59
|
+
? React.null
|
|
60
|
+
: <Box flexDirection={Row}>
|
|
61
|
+
<Text color={Gray}> {eventsText->String.trim->React.string} </Text>
|
|
62
|
+
</Box>}
|
|
63
|
+
<Newline />
|
|
64
|
+
</Box>
|
|
65
|
+
| (_, _, _) =>
|
|
66
|
+
<>
|
|
67
|
+
<Box flexDirection=Row width=Num(chainsWidth)>
|
|
68
|
+
<Box width={Num(headerWidth)}>
|
|
69
|
+
<Text> {"Chain: "->React.string} </Text>
|
|
70
|
+
<Text bold=true> {chainId->React.string} </Text>
|
|
71
|
+
<Text> {" "->React.string} </Text>
|
|
72
|
+
{poweredByHyperSync ? <Text color=Secondary> {"⚡"->React.string} </Text> : React.null}
|
|
73
|
+
</Box>
|
|
74
|
+
<Text> {"Loading progress..."->React.string} </Text>
|
|
75
|
+
</Box>
|
|
76
|
+
<Newline />
|
|
77
|
+
</>
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module TotalEventsProcessed = {
|
|
83
|
+
@react.component
|
|
84
|
+
let make = (~totalEventsProcessed) => {
|
|
85
|
+
let label = "Total Events: "
|
|
86
|
+
<Text>
|
|
87
|
+
<Text bold=true> {label->React.string} </Text>
|
|
88
|
+
<Text color={Secondary}>
|
|
89
|
+
{`${totalEventsProcessed->TuiData.formatFloatLocaleString}`->React.string}
|
|
90
|
+
</Text>
|
|
91
|
+
</Text>
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
module App = {
|
|
96
|
+
@react.component
|
|
97
|
+
let make = (~getState) => {
|
|
98
|
+
let stdoutColumns = Hooks.useStdoutColumns()
|
|
99
|
+
let (state: GlobalState.t, setState) = React.useState(() => getState())
|
|
100
|
+
|
|
101
|
+
// useEffect to refresh state every 500ms
|
|
102
|
+
React.useEffect(() => {
|
|
103
|
+
let intervalId = Js.Global.setInterval(() => {
|
|
104
|
+
setState(_ => getState())
|
|
105
|
+
}, 500)
|
|
106
|
+
|
|
107
|
+
Some(
|
|
108
|
+
() => {
|
|
109
|
+
Js.Global.clearInterval(intervalId)
|
|
110
|
+
},
|
|
111
|
+
)
|
|
112
|
+
}, [getState])
|
|
113
|
+
|
|
114
|
+
let chains =
|
|
115
|
+
state.chainManager.chainFetchers
|
|
116
|
+
->ChainMap.values
|
|
117
|
+
->Array.map(cf => {
|
|
118
|
+
let {numEventsProcessed, fetchState} = cf
|
|
119
|
+
let latestFetchedBlockNumber = Pervasives.max(fetchState->FetchState.bufferBlockNumber, 0)
|
|
120
|
+
let hasProcessedToEndblock = cf->ChainFetcher.hasProcessedToEndblock
|
|
121
|
+
let knownHeight =
|
|
122
|
+
cf->ChainFetcher.hasProcessedToEndblock
|
|
123
|
+
? cf.fetchState.endBlock->Option.getWithDefault(cf.fetchState.knownHeight)
|
|
124
|
+
: cf.fetchState.knownHeight
|
|
125
|
+
|
|
126
|
+
let firstEventBlock = cf.fetchState.firstEventBlock
|
|
127
|
+
let progress: TuiData.progress = if hasProcessedToEndblock {
|
|
128
|
+
// If the endblock has been reached then set the progress to synced.
|
|
129
|
+
// if there's chains that have no events in the block range start->end,
|
|
130
|
+
// it's possible there are no events in that block range (ie firstEventBlock = None)
|
|
131
|
+
// This ensures TUI still displays synced in this case
|
|
132
|
+
Synced({
|
|
133
|
+
firstEventBlockNumber: firstEventBlock->Option.getWithDefault(0),
|
|
134
|
+
latestProcessedBlock: cf.committedProgressBlockNumber,
|
|
135
|
+
timestampCaughtUpToHeadOrEndblock: cf.timestampCaughtUpToHeadOrEndblock->Option.getWithDefault(
|
|
136
|
+
Js.Date.now()->Js.Date.fromFloat,
|
|
137
|
+
),
|
|
138
|
+
numEventsProcessed,
|
|
139
|
+
})
|
|
140
|
+
} else {
|
|
141
|
+
switch (firstEventBlock, cf.timestampCaughtUpToHeadOrEndblock) {
|
|
142
|
+
| (Some(firstEventBlockNumber), Some(timestampCaughtUpToHeadOrEndblock)) =>
|
|
143
|
+
Synced({
|
|
144
|
+
firstEventBlockNumber,
|
|
145
|
+
latestProcessedBlock: cf.committedProgressBlockNumber,
|
|
146
|
+
timestampCaughtUpToHeadOrEndblock,
|
|
147
|
+
numEventsProcessed,
|
|
148
|
+
})
|
|
149
|
+
| (Some(firstEventBlockNumber), None) =>
|
|
150
|
+
Syncing({
|
|
151
|
+
firstEventBlockNumber,
|
|
152
|
+
latestProcessedBlock: cf.committedProgressBlockNumber,
|
|
153
|
+
numEventsProcessed,
|
|
154
|
+
})
|
|
155
|
+
| (None, _) => SearchingForEvents
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
(
|
|
160
|
+
{
|
|
161
|
+
progress,
|
|
162
|
+
knownHeight,
|
|
163
|
+
latestFetchedBlockNumber,
|
|
164
|
+
eventsProcessed: numEventsProcessed,
|
|
165
|
+
chainId: cf.chainConfig.id->Int.toString,
|
|
166
|
+
progressBlock: cf.committedProgressBlockNumber < cf.fetchState.startBlock
|
|
167
|
+
? Some(cf.fetchState.startBlock)
|
|
168
|
+
: Some(cf.committedProgressBlockNumber),
|
|
169
|
+
bufferBlock: Some(latestFetchedBlockNumber),
|
|
170
|
+
sourceBlock: Some(cf.fetchState.knownHeight),
|
|
171
|
+
firstEventBlockNumber: cf.fetchState.firstEventBlock,
|
|
172
|
+
startBlock: cf.fetchState.startBlock,
|
|
173
|
+
endBlock: cf.fetchState.endBlock,
|
|
174
|
+
poweredByHyperSync: (
|
|
175
|
+
cf.sourceManager->SourceManager.getActiveSource
|
|
176
|
+
).poweredByHyperSync,
|
|
177
|
+
}: TuiData.chain
|
|
178
|
+
)
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
let totalEventsProcessed = chains->Array.reduce(0., (acc, chain) => {
|
|
182
|
+
acc +. chain.eventsProcessed
|
|
183
|
+
})
|
|
184
|
+
let maxChainIdLength = chains->Array.reduce(0, (acc, chain) => {
|
|
185
|
+
let chainIdLength = chain.chainId->String.length
|
|
186
|
+
if chainIdLength > acc {
|
|
187
|
+
chainIdLength
|
|
188
|
+
} else {
|
|
189
|
+
acc
|
|
190
|
+
}
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
<Box flexDirection={Column}>
|
|
194
|
+
<BigText
|
|
195
|
+
text="envio"
|
|
196
|
+
colors=[Secondary, Primary]
|
|
197
|
+
font={chains->Array.length > 5 ? Tiny : Block}
|
|
198
|
+
space=false
|
|
199
|
+
/>
|
|
200
|
+
<Newline />
|
|
201
|
+
{chains
|
|
202
|
+
->Array.mapWithIndex((i, chainData) => {
|
|
203
|
+
<ChainLine
|
|
204
|
+
key={i->Int.toString}
|
|
205
|
+
chainId={chainData.chainId}
|
|
206
|
+
maxChainIdLength={maxChainIdLength}
|
|
207
|
+
progressBlock={chainData.progressBlock}
|
|
208
|
+
bufferBlock={chainData.bufferBlock}
|
|
209
|
+
sourceBlock={chainData.sourceBlock}
|
|
210
|
+
startBlock={chainData.startBlock}
|
|
211
|
+
endBlock={chainData.endBlock}
|
|
212
|
+
stdoutColumns={stdoutColumns}
|
|
213
|
+
poweredByHyperSync={chainData.poweredByHyperSync}
|
|
214
|
+
eventsProcessed={chainData.eventsProcessed}
|
|
215
|
+
/>
|
|
216
|
+
})
|
|
217
|
+
->React.array}
|
|
218
|
+
<TotalEventsProcessed totalEventsProcessed />
|
|
219
|
+
<SyncETA chains indexerStartTime=state.indexerStartTime />
|
|
220
|
+
<Newline />
|
|
221
|
+
<Box flexDirection={Row}>
|
|
222
|
+
<Text> {"GraphQL: "->React.string} </Text>
|
|
223
|
+
<Text color={Info} underline=true> {Env.Hasura.url->React.string} </Text>
|
|
224
|
+
{
|
|
225
|
+
let defaultPassword = "testing"
|
|
226
|
+
if Env.Hasura.secret == defaultPassword {
|
|
227
|
+
<Text color={Gray}> {` (password: ${defaultPassword})`->React.string} </Text>
|
|
228
|
+
} else {
|
|
229
|
+
React.null
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
</Box>
|
|
233
|
+
<Box flexDirection={Row}>
|
|
234
|
+
<Text> {"Dev Console: "->React.string} </Text>
|
|
235
|
+
<Text color={Info} underline=true> {`${Env.envioAppUrl}/console`->React.string} </Text>
|
|
236
|
+
</Box>
|
|
237
|
+
<Messages config=state.ctx.config />
|
|
238
|
+
</Box>
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
let start = (~getState) => {
|
|
243
|
+
let {rerender} = render(<App getState />)
|
|
244
|
+
() => {
|
|
245
|
+
rerender(<App getState />)
|
|
246
|
+
}
|
|
247
|
+
}
|