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.
Files changed (175) hide show
  1. package/README.md +164 -30
  2. package/bin.mjs +49 -0
  3. package/evm.schema.json +79 -169
  4. package/fuel.schema.json +50 -21
  5. package/index.d.ts +497 -1
  6. package/index.js +4 -0
  7. package/package.json +42 -31
  8. package/rescript.json +4 -1
  9. package/src/Batch.res +11 -8
  10. package/src/Batch.res.mjs +11 -9
  11. package/src/ChainFetcher.res +531 -0
  12. package/src/ChainFetcher.res.mjs +339 -0
  13. package/src/ChainManager.res +190 -0
  14. package/src/ChainManager.res.mjs +166 -0
  15. package/src/Change.res +3 -3
  16. package/src/Config.gen.ts +19 -0
  17. package/src/Config.res +737 -22
  18. package/src/Config.res.mjs +703 -26
  19. package/src/{Indexer.res → Ctx.res} +1 -1
  20. package/src/Ecosystem.res +9 -124
  21. package/src/Ecosystem.res.mjs +19 -160
  22. package/src/Env.res +30 -74
  23. package/src/Env.res.mjs +25 -87
  24. package/src/Envio.gen.ts +3 -1
  25. package/src/Envio.res +20 -9
  26. package/src/EventProcessing.res +469 -0
  27. package/src/EventProcessing.res.mjs +337 -0
  28. package/src/EvmTypes.gen.ts +6 -0
  29. package/src/EvmTypes.res +1 -0
  30. package/src/FetchState.res +1256 -639
  31. package/src/FetchState.res.mjs +1135 -612
  32. package/src/GlobalState.res +1190 -0
  33. package/src/GlobalState.res.mjs +1183 -0
  34. package/src/GlobalStateManager.res +68 -0
  35. package/src/GlobalStateManager.res.mjs +75 -0
  36. package/src/GlobalStateManager.resi +7 -0
  37. package/src/HandlerLoader.res +89 -0
  38. package/src/HandlerLoader.res.mjs +79 -0
  39. package/src/HandlerRegister.res +357 -0
  40. package/src/HandlerRegister.res.mjs +299 -0
  41. package/src/{EventRegister.resi → HandlerRegister.resi} +13 -13
  42. package/src/Hasura.res +111 -175
  43. package/src/Hasura.res.mjs +88 -150
  44. package/src/InMemoryStore.res +1 -1
  45. package/src/InMemoryStore.res.mjs +3 -3
  46. package/src/InMemoryTable.res +1 -1
  47. package/src/InMemoryTable.res.mjs +1 -1
  48. package/src/Internal.gen.ts +4 -0
  49. package/src/Internal.res +230 -12
  50. package/src/Internal.res.mjs +115 -1
  51. package/src/LoadLayer.res +444 -0
  52. package/src/LoadLayer.res.mjs +296 -0
  53. package/src/LoadLayer.resi +32 -0
  54. package/src/LogSelection.res +33 -27
  55. package/src/LogSelection.res.mjs +6 -0
  56. package/src/Logging.res +21 -7
  57. package/src/Logging.res.mjs +16 -8
  58. package/src/Main.res +377 -0
  59. package/src/Main.res.mjs +339 -0
  60. package/src/Persistence.res +7 -21
  61. package/src/Persistence.res.mjs +3 -3
  62. package/src/PgStorage.gen.ts +10 -0
  63. package/src/PgStorage.res +116 -69
  64. package/src/PgStorage.res.d.mts +5 -0
  65. package/src/PgStorage.res.mjs +93 -50
  66. package/src/Prometheus.res +294 -224
  67. package/src/Prometheus.res.mjs +353 -340
  68. package/src/ReorgDetection.res +6 -10
  69. package/src/ReorgDetection.res.mjs +6 -6
  70. package/src/SafeCheckpointTracking.res +4 -4
  71. package/src/SafeCheckpointTracking.res.mjs +2 -2
  72. package/src/Sink.res +4 -2
  73. package/src/Sink.res.mjs +2 -1
  74. package/src/TableIndices.res +0 -1
  75. package/src/TestIndexer.res +692 -0
  76. package/src/TestIndexer.res.mjs +527 -0
  77. package/src/TestIndexerProxyStorage.res +205 -0
  78. package/src/TestIndexerProxyStorage.res.mjs +151 -0
  79. package/src/TopicFilter.res +1 -1
  80. package/src/Types.ts +1 -1
  81. package/src/UserContext.res +424 -0
  82. package/src/UserContext.res.mjs +279 -0
  83. package/src/Utils.res +97 -26
  84. package/src/Utils.res.mjs +91 -44
  85. package/src/bindings/BigInt.res +10 -0
  86. package/src/bindings/BigInt.res.mjs +15 -0
  87. package/src/bindings/ClickHouse.res +120 -23
  88. package/src/bindings/ClickHouse.res.mjs +118 -28
  89. package/src/bindings/DateFns.res +74 -0
  90. package/src/bindings/DateFns.res.mjs +22 -0
  91. package/src/bindings/EventSource.res +8 -1
  92. package/src/bindings/EventSource.res.mjs +8 -1
  93. package/src/bindings/Express.res +1 -0
  94. package/src/bindings/Hrtime.res +14 -1
  95. package/src/bindings/Hrtime.res.mjs +22 -2
  96. package/src/bindings/Hrtime.resi +4 -0
  97. package/src/bindings/Lodash.res +0 -1
  98. package/src/bindings/NodeJs.res +49 -3
  99. package/src/bindings/NodeJs.res.mjs +11 -3
  100. package/src/bindings/Pino.res +24 -10
  101. package/src/bindings/Pino.res.mjs +14 -8
  102. package/src/bindings/Postgres.gen.ts +8 -0
  103. package/src/bindings/Postgres.res +5 -1
  104. package/src/bindings/Postgres.res.d.mts +5 -0
  105. package/src/bindings/PromClient.res +0 -10
  106. package/src/bindings/PromClient.res.mjs +0 -3
  107. package/src/bindings/Vitest.res +142 -0
  108. package/src/bindings/Vitest.res.mjs +9 -0
  109. package/src/bindings/WebSocket.res +27 -0
  110. package/src/bindings/WebSocket.res.mjs +2 -0
  111. package/src/bindings/Yargs.res +8 -0
  112. package/src/bindings/Yargs.res.mjs +2 -0
  113. package/src/db/EntityHistory.res +7 -7
  114. package/src/db/EntityHistory.res.mjs +9 -9
  115. package/src/db/InternalTable.res +59 -111
  116. package/src/db/InternalTable.res.mjs +73 -104
  117. package/src/db/Table.res +27 -8
  118. package/src/db/Table.res.mjs +25 -14
  119. package/src/sources/Evm.res +84 -0
  120. package/src/sources/Evm.res.mjs +105 -0
  121. package/src/sources/EvmChain.res +94 -0
  122. package/src/sources/EvmChain.res.mjs +60 -0
  123. package/src/sources/Fuel.res +19 -34
  124. package/src/sources/Fuel.res.mjs +34 -16
  125. package/src/sources/FuelSDK.res +38 -0
  126. package/src/sources/FuelSDK.res.mjs +29 -0
  127. package/src/sources/HyperFuel.res +2 -2
  128. package/src/sources/HyperFuel.resi +1 -1
  129. package/src/sources/HyperFuelClient.res +2 -2
  130. package/src/sources/HyperFuelSource.res +33 -13
  131. package/src/sources/HyperFuelSource.res.mjs +24 -16
  132. package/src/sources/HyperSync.res +36 -6
  133. package/src/sources/HyperSync.res.mjs +9 -7
  134. package/src/sources/HyperSync.resi +4 -0
  135. package/src/sources/HyperSyncClient.res +1 -1
  136. package/src/sources/HyperSyncHeightStream.res +47 -116
  137. package/src/sources/HyperSyncHeightStream.res.mjs +46 -73
  138. package/src/sources/HyperSyncSource.res +118 -139
  139. package/src/sources/HyperSyncSource.res.mjs +104 -121
  140. package/src/sources/Rpc.res +86 -14
  141. package/src/sources/Rpc.res.mjs +101 -9
  142. package/src/sources/RpcSource.res +621 -364
  143. package/src/sources/RpcSource.res.mjs +843 -410
  144. package/src/sources/RpcWebSocketHeightStream.res +181 -0
  145. package/src/sources/RpcWebSocketHeightStream.res.mjs +196 -0
  146. package/src/sources/Source.res +7 -5
  147. package/src/sources/SourceManager.res +325 -225
  148. package/src/sources/SourceManager.res.mjs +314 -171
  149. package/src/sources/SourceManager.resi +17 -6
  150. package/src/sources/Svm.res +81 -0
  151. package/src/sources/Svm.res.mjs +90 -0
  152. package/src/tui/Tui.res +247 -0
  153. package/src/tui/Tui.res.mjs +337 -0
  154. package/src/tui/bindings/Ink.res +371 -0
  155. package/src/tui/bindings/Ink.res.mjs +72 -0
  156. package/src/tui/bindings/Style.res +123 -0
  157. package/src/tui/bindings/Style.res.mjs +2 -0
  158. package/src/tui/components/BufferedProgressBar.res +40 -0
  159. package/src/tui/components/BufferedProgressBar.res.mjs +57 -0
  160. package/src/tui/components/CustomHooks.res +122 -0
  161. package/src/tui/components/CustomHooks.res.mjs +179 -0
  162. package/src/tui/components/Messages.res +41 -0
  163. package/src/tui/components/Messages.res.mjs +75 -0
  164. package/src/tui/components/SyncETA.res +174 -0
  165. package/src/tui/components/SyncETA.res.mjs +263 -0
  166. package/src/tui/components/TuiData.res +47 -0
  167. package/src/tui/components/TuiData.res.mjs +34 -0
  168. package/svm.schema.json +112 -0
  169. package/bin.js +0 -48
  170. package/src/EventRegister.res +0 -241
  171. package/src/EventRegister.res.mjs +0 -240
  172. package/src/bindings/Ethers.gen.ts +0 -14
  173. package/src/bindings/Ethers.res +0 -204
  174. package/src/bindings/Ethers.res.mjs +0 -130
  175. /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
- ~newBlockFallbackStallTimeout: int=?,
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: (~currentBlockHeight: int) => promise<int>,
19
- ~onNewBlock: (~currentBlockHeight: int) => unit,
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, ~currentBlockHeight: int) => promise<int>
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
- ~currentBlockHeight: int,
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 */
@@ -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
+ }