envio 3.1.2 → 3.2.1

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 (134) hide show
  1. package/evm.schema.json +83 -11
  2. package/fuel.schema.json +83 -11
  3. package/index.d.ts +184 -3
  4. package/package.json +6 -6
  5. package/src/Batch.res +2 -2
  6. package/src/ChainFetcher.res +27 -3
  7. package/src/ChainFetcher.res.mjs +17 -3
  8. package/src/ChainManager.res +163 -0
  9. package/src/ChainManager.res.mjs +136 -0
  10. package/src/Config.res +213 -30
  11. package/src/Config.res.mjs +102 -41
  12. package/src/Core.res +16 -10
  13. package/src/Ecosystem.res +0 -3
  14. package/src/Env.res +2 -2
  15. package/src/Env.res.mjs +2 -2
  16. package/src/Envio.res +101 -2
  17. package/src/Envio.res.mjs +2 -3
  18. package/src/EventConfigBuilder.res +87 -0
  19. package/src/EventConfigBuilder.res.mjs +53 -0
  20. package/src/EventUtils.res +2 -2
  21. package/src/FetchState.res +63 -67
  22. package/src/FetchState.res.mjs +44 -42
  23. package/src/GlobalState.res +219 -363
  24. package/src/GlobalState.res.mjs +314 -491
  25. package/src/GlobalStateManager.res +49 -59
  26. package/src/GlobalStateManager.res.mjs +5 -4
  27. package/src/GlobalStateManager.resi +1 -1
  28. package/src/HandlerLoader.res +18 -2
  29. package/src/HandlerLoader.res.mjs +16 -34
  30. package/src/HandlerRegister.res +9 -9
  31. package/src/HandlerRegister.res.mjs +9 -9
  32. package/src/Hasura.res +102 -32
  33. package/src/Hasura.res.mjs +88 -34
  34. package/src/InMemoryStore.res +10 -1
  35. package/src/InMemoryStore.res.mjs +4 -1
  36. package/src/InMemoryTable.res +83 -136
  37. package/src/InMemoryTable.res.mjs +57 -86
  38. package/src/Internal.res +70 -5
  39. package/src/Internal.res.mjs +2 -8
  40. package/src/LazyLoader.res +2 -2
  41. package/src/LazyLoader.res.mjs +3 -3
  42. package/src/LoadLayer.res +47 -60
  43. package/src/LoadLayer.res.mjs +28 -50
  44. package/src/LoadLayer.resi +2 -5
  45. package/src/LogSelection.res +90 -21
  46. package/src/LogSelection.res.mjs +72 -21
  47. package/src/Logging.res +1 -1
  48. package/src/Main.res +61 -2
  49. package/src/Main.res.mjs +37 -1
  50. package/src/Persistence.res +3 -16
  51. package/src/PgStorage.res +125 -114
  52. package/src/PgStorage.res.mjs +112 -95
  53. package/src/Ports.res +5 -0
  54. package/src/Ports.res.mjs +9 -0
  55. package/src/Prometheus.res +3 -3
  56. package/src/Prometheus.res.mjs +4 -4
  57. package/src/ReorgDetection.res +4 -4
  58. package/src/ReorgDetection.res.mjs +4 -5
  59. package/src/SafeCheckpointTracking.res +16 -16
  60. package/src/SafeCheckpointTracking.res.mjs +2 -2
  61. package/src/SimulateItems.res +10 -14
  62. package/src/SimulateItems.res.mjs +5 -2
  63. package/src/Sink.res +1 -1
  64. package/src/Sink.res.mjs +1 -2
  65. package/src/SvmTypes.res +9 -0
  66. package/src/SvmTypes.res.mjs +14 -0
  67. package/src/TestIndexer.res +35 -68
  68. package/src/TestIndexer.res.mjs +17 -48
  69. package/src/TestIndexerProxyStorage.res +23 -23
  70. package/src/TestIndexerProxyStorage.res.mjs +12 -15
  71. package/src/Throttler.res +2 -2
  72. package/src/Time.res +2 -2
  73. package/src/Time.res.mjs +2 -2
  74. package/src/UserContext.res +19 -118
  75. package/src/UserContext.res.mjs +10 -66
  76. package/src/Utils.res +15 -15
  77. package/src/Utils.res.mjs +7 -8
  78. package/src/adapters/MarkBatchProcessedAdapter.res +5 -0
  79. package/src/adapters/MarkBatchProcessedAdapter.res.mjs +14 -0
  80. package/src/bindings/BigDecimal.res +1 -1
  81. package/src/bindings/BigDecimal.res.mjs +2 -2
  82. package/src/bindings/ClickHouse.res +8 -6
  83. package/src/bindings/ClickHouse.res.mjs +5 -5
  84. package/src/bindings/Hrtime.res +1 -1
  85. package/src/bindings/Pino.res +2 -2
  86. package/src/bindings/Pino.res.mjs +3 -4
  87. package/src/db/EntityFilter.res +410 -0
  88. package/src/db/EntityFilter.res.mjs +424 -0
  89. package/src/db/EntityHistory.res +1 -1
  90. package/src/db/EntityHistory.res.mjs +1 -1
  91. package/src/db/InternalTable.res +10 -10
  92. package/src/db/InternalTable.res.mjs +41 -45
  93. package/src/db/Schema.res +2 -2
  94. package/src/db/Schema.res.mjs +3 -3
  95. package/src/db/Table.res +106 -22
  96. package/src/db/Table.res.mjs +84 -35
  97. package/src/sources/EventRouter.res +67 -2
  98. package/src/sources/EventRouter.res.mjs +45 -3
  99. package/src/sources/Evm.res +0 -7
  100. package/src/sources/Evm.res.mjs +0 -15
  101. package/src/sources/EvmChain.res +1 -1
  102. package/src/sources/EvmChain.res.mjs +1 -2
  103. package/src/sources/EvmRpcClient.res +42 -0
  104. package/src/sources/EvmRpcClient.res.mjs +64 -0
  105. package/src/sources/Fuel.res +0 -7
  106. package/src/sources/Fuel.res.mjs +0 -15
  107. package/src/sources/HyperFuelSource.res +5 -4
  108. package/src/sources/HyperFuelSource.res.mjs +2 -2
  109. package/src/sources/HyperSyncClient.res +9 -5
  110. package/src/sources/HyperSyncClient.res.mjs +2 -2
  111. package/src/sources/HyperSyncHeightStream.res +2 -2
  112. package/src/sources/HyperSyncHeightStream.res.mjs +2 -2
  113. package/src/sources/HyperSyncSource.res +12 -11
  114. package/src/sources/HyperSyncSource.res.mjs +6 -6
  115. package/src/sources/Rpc.res +1 -5
  116. package/src/sources/Rpc.res.mjs +1 -9
  117. package/src/sources/RpcSource.res +57 -21
  118. package/src/sources/RpcSource.res.mjs +47 -20
  119. package/src/sources/RpcWebSocketHeightStream.res +1 -1
  120. package/src/sources/SourceManager.res +3 -2
  121. package/src/sources/SourceManager.res.mjs +1 -1
  122. package/src/sources/Svm.res +3 -10
  123. package/src/sources/Svm.res.mjs +4 -18
  124. package/src/sources/SvmHyperSyncClient.res +265 -0
  125. package/src/sources/SvmHyperSyncClient.res.mjs +28 -0
  126. package/src/sources/SvmHyperSyncSource.res +638 -0
  127. package/src/sources/SvmHyperSyncSource.res.mjs +557 -0
  128. package/src/tui/Tui.res +9 -2
  129. package/src/tui/Tui.res.mjs +18 -3
  130. package/src/tui/components/BufferedProgressBar.res +2 -2
  131. package/src/tui/components/TuiData.res +3 -0
  132. package/svm.schema.json +523 -14
  133. package/src/TableIndices.res +0 -115
  134. package/src/TableIndices.res.mjs +0 -144
@@ -0,0 +1,557 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Hrtime from "../bindings/Hrtime.res.mjs";
4
+ import * as Source from "./Source.res.mjs";
5
+ import * as Prometheus from "../Prometheus.res.mjs";
6
+ import * as EventRouter from "./EventRouter.res.mjs";
7
+ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
8
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
9
+ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
10
+ import * as SvmHyperSyncClient from "./SvmHyperSyncClient.res.mjs";
11
+ import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
12
+
13
+ function buildInstructionSelections(eventConfigs) {
14
+ return eventConfigs.flatMap(cfg => {
15
+ let programIdString = cfg.programId;
16
+ if (programIdString === "") {
17
+ return [];
18
+ }
19
+ let match = cfg.discriminator;
20
+ let match$1 = cfg.discriminatorByteLen;
21
+ let match$2;
22
+ if (match !== undefined) {
23
+ switch (match$1) {
24
+ case 1 :
25
+ match$2 = [
26
+ [match],
27
+ undefined,
28
+ undefined,
29
+ undefined
30
+ ];
31
+ break;
32
+ case 2 :
33
+ match$2 = [
34
+ undefined,
35
+ [match],
36
+ undefined,
37
+ undefined
38
+ ];
39
+ break;
40
+ case 4 :
41
+ match$2 = [
42
+ undefined,
43
+ undefined,
44
+ [match],
45
+ undefined
46
+ ];
47
+ break;
48
+ case 8 :
49
+ match$2 = [
50
+ undefined,
51
+ undefined,
52
+ undefined,
53
+ [match]
54
+ ];
55
+ break;
56
+ default:
57
+ match$2 = [
58
+ undefined,
59
+ undefined,
60
+ undefined,
61
+ undefined
62
+ ];
63
+ }
64
+ } else {
65
+ match$2 = [
66
+ undefined,
67
+ undefined,
68
+ undefined,
69
+ undefined
70
+ ];
71
+ }
72
+ let d8 = match$2[3];
73
+ let d4 = match$2[2];
74
+ let d2 = match$2[1];
75
+ let d1 = match$2[0];
76
+ let gs = cfg.accountFilters;
77
+ let groups = gs.length !== 0 ? gs : [[]];
78
+ return groups.map(group => {
79
+ let pick = position => Stdlib_Array.filterMap(group, f => {
80
+ if (f.position === position) {
81
+ return f.values;
82
+ }
83
+ })[0];
84
+ return {
85
+ programId: [programIdString],
86
+ d1: d1,
87
+ d2: d2,
88
+ d4: d4,
89
+ d8: d8,
90
+ a0: pick(0),
91
+ a1: pick(1),
92
+ a2: pick(2),
93
+ a3: pick(3),
94
+ a4: pick(4),
95
+ a5: pick(5),
96
+ isInner: cfg.isInner
97
+ };
98
+ });
99
+ });
100
+ }
101
+
102
+ function synthLogIndex(instr) {
103
+ let tx = instr.transactionIndex;
104
+ let addrSum = Stdlib_Array.reduce(instr.instructionAddress, 0, (acc, n) => ((acc << 10) + n | 0) + 1 | 0);
105
+ return (tx << 16) + addrSum | 0;
106
+ }
107
+
108
+ function serializeInstructionAddress(addr) {
109
+ return addr.map(n => n.toString()).join(",");
110
+ }
111
+
112
+ function buildProgramSchemas(eventConfigs) {
113
+ let descriptorsByProgram = {};
114
+ eventConfigs.forEach(ec => {
115
+ let programIdString = ec.programId;
116
+ if (programIdString === "") {
117
+ return;
118
+ }
119
+ let hasSchema = ec.accounts.length !== 0 || ec.args !== null;
120
+ let discriminator = Stdlib_Option.getOr(ec.discriminator, "");
121
+ if (!(hasSchema && discriminator !== "")) {
122
+ return;
123
+ }
124
+ let other = ec.definedTypes;
125
+ let definedTypes;
126
+ definedTypes = other === null ? ({}) : other;
127
+ let existing = descriptorsByProgram[programIdString];
128
+ let descriptor = existing !== undefined ? Primitive_option.valFromOption(existing) : ({
129
+ programId: programIdString,
130
+ definedTypes: definedTypes,
131
+ instructions: []
132
+ });
133
+ let instruction = {
134
+ name: ec.name,
135
+ discriminator: discriminator,
136
+ accounts: ec.accounts,
137
+ args: ec.args
138
+ };
139
+ descriptorsByProgram[programIdString] = {
140
+ programId: descriptor.programId,
141
+ definedTypes: descriptor.definedTypes,
142
+ instructions: descriptor.instructions.concat([instruction])
143
+ };
144
+ });
145
+ return Object.values(descriptorsByProgram).map(descriptor => JSON.stringify(descriptor));
146
+ }
147
+
148
+ function parseDecoded(d) {
149
+ let args;
150
+ try {
151
+ args = JSON.parse(d.argsJson);
152
+ } catch (exn) {
153
+ args = {};
154
+ }
155
+ let accounts;
156
+ try {
157
+ accounts = JSON.parse(d.accountsJson);
158
+ } catch (exn$1) {
159
+ accounts = {};
160
+ }
161
+ return {
162
+ name: d.name,
163
+ args: args,
164
+ accounts: accounts,
165
+ extraAccounts: d.extraAccounts
166
+ };
167
+ }
168
+
169
+ function toSvmInstruction(instr, programName, instructionName, transaction, logs, block) {
170
+ return {
171
+ programName: programName,
172
+ instructionName: instructionName,
173
+ programId: instr.programId,
174
+ data: instr.data,
175
+ accounts: instr.accounts,
176
+ instructionAddress: instr.instructionAddress,
177
+ isInner: instr.isInner,
178
+ d1: instr.d1,
179
+ d2: instr.d2,
180
+ d4: instr.d4,
181
+ d8: instr.d8,
182
+ params: Stdlib_Option.map(instr.decoded, parseDecoded),
183
+ transaction: transaction,
184
+ logs: logs,
185
+ block: block
186
+ };
187
+ }
188
+
189
+ function toSvmTransaction(tx) {
190
+ return {
191
+ signatures: tx.signatures,
192
+ feePayer: Stdlib_Option.map(tx.feePayer, prim => prim),
193
+ success: tx.success,
194
+ err: tx.err,
195
+ fee: Stdlib_Option.map(tx.fee, prim => BigInt(prim)),
196
+ computeUnitsConsumed: Stdlib_Option.map(tx.computeUnitsConsumed, prim => BigInt(prim)),
197
+ accountKeys: tx.accountKeys,
198
+ recentBlockhash: tx.recentBlockhash,
199
+ version: tx.version
200
+ };
201
+ }
202
+
203
+ function toSvmTokenBalance(tb) {
204
+ return {
205
+ account: Stdlib_Option.map(tb.account, prim => prim),
206
+ mint: Stdlib_Option.map(tb.mint, prim => prim),
207
+ owner: Stdlib_Option.map(tb.owner, prim => prim),
208
+ preAmount: tb.preAmount,
209
+ postAmount: tb.postAmount
210
+ };
211
+ }
212
+
213
+ function probeRouter(router, programId, instr, byteLengthsDesc, contractAddress, indexingAddresses) {
214
+ let probe = dN => {
215
+ let tag = EventRouter.getSvmEventId(programId, dN);
216
+ return EventRouter.get(router, tag, contractAddress, instr.slot, indexingAddresses);
217
+ };
218
+ let result = Stdlib_Array.reduce(byteLengthsDesc, undefined, (acc, len) => {
219
+ if (acc !== undefined) {
220
+ return acc;
221
+ }
222
+ let candidate;
223
+ switch (len) {
224
+ case 1 :
225
+ candidate = instr.d1;
226
+ break;
227
+ case 2 :
228
+ candidate = instr.d2;
229
+ break;
230
+ case 4 :
231
+ candidate = instr.d4;
232
+ break;
233
+ case 8 :
234
+ candidate = instr.d8;
235
+ break;
236
+ default:
237
+ candidate = undefined;
238
+ }
239
+ if (candidate !== undefined) {
240
+ return probe(candidate);
241
+ }
242
+ });
243
+ if (result !== undefined) {
244
+ return result;
245
+ } else {
246
+ return probe(undefined);
247
+ }
248
+ }
249
+
250
+ function make(param) {
251
+ let eventConfigs = param.eventConfigs;
252
+ let chain = param.chain;
253
+ let name = "SvmHyperSync";
254
+ let programSchemas = buildProgramSchemas(eventConfigs);
255
+ let client = SvmHyperSyncClient.make(param.endpointUrl, param.apiToken, param.clientTimeoutMillis, undefined, undefined, undefined, programSchemas.length !== 0 ? programSchemas : undefined);
256
+ let match = EventRouter.fromSvmEventConfigsOrThrow(eventConfigs, chain);
257
+ let eventRouter = match[0];
258
+ let orderingByProgram = {};
259
+ match[1].forEach(o => {
260
+ orderingByProgram[o.programId] = o.byteLengthsDesc;
261
+ });
262
+ let needsTransactions = eventConfigs.some(cfg => cfg.includeTransaction);
263
+ let needsLogs = eventConfigs.some(cfg => cfg.includeLogs);
264
+ let needsTokenBalances = eventConfigs.some(cfg => cfg.includeTokenBalances);
265
+ let getItemsOrThrow = async (fromBlock, toBlock, param, indexingAddresses, knownHeight, param$1, param$2, retry, logger) => {
266
+ let totalTimeRef = Hrtime.makeTimer();
267
+ let pageFetchRef = Hrtime.makeTimer();
268
+ let instructionSelections = buildInstructionSelections(eventConfigs);
269
+ let fields_block = [
270
+ "slot",
271
+ "blockhash",
272
+ "block_time"
273
+ ];
274
+ let fields_transaction = needsTransactions ? [
275
+ "slot",
276
+ "transaction_index",
277
+ "signatures",
278
+ "fee_payer",
279
+ "success",
280
+ "err",
281
+ "fee",
282
+ "compute_units_consumed",
283
+ "account_keys",
284
+ "recent_blockhash",
285
+ "version"
286
+ ] : undefined;
287
+ let fields_log = needsLogs ? [
288
+ "slot",
289
+ "transaction_index",
290
+ "instruction_address",
291
+ "kind",
292
+ "message"
293
+ ] : undefined;
294
+ let fields_tokenBalance = needsTokenBalances ? [
295
+ "slot",
296
+ "transaction_index",
297
+ "account",
298
+ "mint",
299
+ "owner",
300
+ "pre_amount",
301
+ "post_amount"
302
+ ] : undefined;
303
+ let fields = {
304
+ block: fields_block,
305
+ transaction: fields_transaction,
306
+ log: fields_log,
307
+ tokenBalance: fields_tokenBalance
308
+ };
309
+ let query_toSlot = Stdlib_Option.map(toBlock, toBlock => toBlock + 1 | 0);
310
+ let query_instructions = instructionSelections;
311
+ let query_fields = fields;
312
+ let query = {
313
+ fromSlot: fromBlock,
314
+ toSlot: query_toSlot,
315
+ instructions: query_instructions,
316
+ fields: query_fields
317
+ };
318
+ Prometheus.SourceRequestCount.increment(name, chain, "getInstructions");
319
+ let resp;
320
+ try {
321
+ resp = await client.get(query);
322
+ } catch (raw_exn) {
323
+ let exn = Primitive_exceptions.internalToException(raw_exn);
324
+ throw {
325
+ RE_EXN_ID: Source.GetItemsError,
326
+ _1: {
327
+ TAG: "FailedGettingItems",
328
+ exn: exn,
329
+ attemptedToBlock: Stdlib_Option.getOr(toBlock, knownHeight),
330
+ retry: {
331
+ TAG: "WithBackoff",
332
+ message: `Unexpected issue while fetching instructions from SVM HyperSync. Attempt a retry.`,
333
+ backoffMillis: retry !== 0 ? 1000 * retry | 0 : 500
334
+ }
335
+ },
336
+ Error: new Error()
337
+ };
338
+ }
339
+ let pageFetchTime = Hrtime.toSecondsFloat(Hrtime.timeSince(pageFetchRef));
340
+ let parsingRef = Hrtime.makeTimer();
341
+ let blockTimeBySlot = {};
342
+ resp.data.blocks.forEach(b => {
343
+ let t = b.blockTime;
344
+ if (t !== undefined) {
345
+ blockTimeBySlot[b.slot.toString()] = t;
346
+ return;
347
+ }
348
+ });
349
+ let txByKey = {};
350
+ resp.data.transactions.forEach(tx => {
351
+ let key = tx.slot.toString() + ":" + tx.transactionIndex.toString();
352
+ txByKey[key] = tx;
353
+ });
354
+ let logsByKey = {};
355
+ resp.data.logs.forEach(log => {
356
+ let match = log.transactionIndex;
357
+ let match$1 = log.instructionAddress;
358
+ if (match === undefined) {
359
+ return;
360
+ }
361
+ if (match$1 === undefined) {
362
+ return;
363
+ }
364
+ let key = log.slot.toString() + ":" + match.toString() + ":" + serializeInstructionAddress(match$1);
365
+ let existing = logsByKey[key];
366
+ if (existing !== undefined) {
367
+ existing.push(log);
368
+ } else {
369
+ logsByKey[key] = [log];
370
+ }
371
+ });
372
+ let tokenBalancesByTx = {};
373
+ if (needsTokenBalances) {
374
+ resp.data.tokenBalances.forEach(tb => {
375
+ let txIdx = tb.transactionIndex;
376
+ if (txIdx === undefined) {
377
+ return;
378
+ }
379
+ let key = tb.slot.toString() + ":" + txIdx.toString();
380
+ let existing = tokenBalancesByTx[key];
381
+ if (existing !== undefined) {
382
+ existing.push(tb);
383
+ } else {
384
+ tokenBalancesByTx[key] = [tb];
385
+ }
386
+ });
387
+ }
388
+ let parsedQueueItems = [];
389
+ resp.data.instructions.forEach(instr => {
390
+ let programId = instr.programId;
391
+ let byteLengths = Stdlib_Option.getOr(orderingByProgram[instr.programId], []);
392
+ let contractAddress = instr.programId;
393
+ let maybeConfig = probeRouter(eventRouter, programId, instr, byteLengths, contractAddress, indexingAddresses);
394
+ if (maybeConfig !== undefined) {
395
+ let txKey = instr.slot.toString() + ":" + instr.transactionIndex.toString();
396
+ let maybeTx = Stdlib_Option.map(txByKey[txKey], toSvmTransaction);
397
+ let maybeTx$1 = maybeConfig.includeTokenBalances ? Stdlib_Option.map(maybeTx, tx => {
398
+ let maybeBalances = Stdlib_Option.map(tokenBalancesByTx[txKey], bals => bals.map(toSvmTokenBalance));
399
+ let newrecord = {...tx};
400
+ newrecord.tokenBalances = maybeBalances;
401
+ return newrecord;
402
+ }) : maybeTx;
403
+ let logKey = instr.slot.toString() + ":" + instr.transactionIndex.toString() + ":" + serializeInstructionAddress(instr.instructionAddress);
404
+ let maybeLogs = Stdlib_Option.map(logsByKey[logKey], logs => logs.map(log => ({
405
+ kind: Stdlib_Option.getOr(log.kind, ""),
406
+ message: Stdlib_Option.getOr(log.message, "")
407
+ })));
408
+ let slotKey = instr.slot.toString();
409
+ let blockTime = blockTimeBySlot[slotKey];
410
+ let payload = toSvmInstruction(instr, maybeConfig.contractName, maybeConfig.name, maybeConfig.includeTransaction ? maybeTx$1 : undefined, maybeConfig.includeLogs ? maybeLogs : undefined, {
411
+ slot: instr.slot,
412
+ time: Stdlib_Option.getOr(blockTime, 0),
413
+ hash: ""
414
+ });
415
+ parsedQueueItems.push({
416
+ kind: 0,
417
+ eventConfig: maybeConfig,
418
+ timestamp: Stdlib_Option.getOr(blockTime, 0),
419
+ chain: chain,
420
+ blockNumber: instr.slot,
421
+ blockHash: "",
422
+ logIndex: synthLogIndex(instr),
423
+ event: payload
424
+ });
425
+ }
426
+ });
427
+ let parsingTimeElapsed = Hrtime.toSecondsFloat(Hrtime.timeSince(parsingRef));
428
+ let highestSlot = resp.nextSlot - 1 | 0;
429
+ let latestBlockTime = Stdlib_Option.getOr(blockTimeBySlot[highestSlot.toString()], 0);
430
+ let blockHashes = resp.data.blocks.map(b => ({
431
+ blockHash: b.blockhash,
432
+ blockNumber: b.slot
433
+ }));
434
+ let totalTimeElapsed = Hrtime.toSecondsFloat(Hrtime.timeSince(totalTimeRef));
435
+ return {
436
+ knownHeight: knownHeight,
437
+ blockHashes: blockHashes,
438
+ parsedQueueItems: parsedQueueItems,
439
+ fromBlockQueried: fromBlock,
440
+ latestFetchedBlockNumber: highestSlot,
441
+ latestFetchedBlockTimestamp: latestBlockTime,
442
+ stats: {
443
+ "total time elapsed (s)": totalTimeElapsed,
444
+ "parsing time (s)": parsingTimeElapsed,
445
+ "page fetch time (s)": pageFetchTime
446
+ }
447
+ };
448
+ };
449
+ let queryBlockDataRange = async (fromSlot, toSlot) => {
450
+ let blockDatas = [];
451
+ let fromRef = fromSlot;
452
+ let keepGoing = true;
453
+ while (keepGoing) {
454
+ let query_toSlot = toSlot + 1 | 0;
455
+ let query_includeAllBlocks = true;
456
+ let query_fields = {
457
+ block: [
458
+ "slot",
459
+ "blockhash",
460
+ "block_time"
461
+ ]
462
+ };
463
+ let query_maxNumBlocks = 1000;
464
+ let query = {
465
+ fromSlot: fromRef,
466
+ toSlot: query_toSlot,
467
+ includeAllBlocks: query_includeAllBlocks,
468
+ fields: query_fields,
469
+ maxNumBlocks: query_maxNumBlocks
470
+ };
471
+ Prometheus.SourceRequestCount.increment(name, chain, "getBlockHashes");
472
+ let resp = await client.get(query);
473
+ resp.data.blocks.forEach(b => {
474
+ blockDatas.push({
475
+ blockHash: b.blockhash,
476
+ blockNumber: b.slot,
477
+ blockTimestamp: Stdlib_Option.getOr(b.blockTime, 0)
478
+ });
479
+ });
480
+ if (resp.nextSlot > toSlot || resp.nextSlot <= fromRef) {
481
+ keepGoing = false;
482
+ } else {
483
+ fromRef = resp.nextSlot;
484
+ }
485
+ };
486
+ return blockDatas;
487
+ };
488
+ let getBlockHashes = async (blockNumbers, param) => {
489
+ let firstSlot = blockNumbers[0];
490
+ if (firstSlot === undefined) {
491
+ return {
492
+ TAG: "Ok",
493
+ _0: []
494
+ };
495
+ }
496
+ try {
497
+ let minSlot = {
498
+ contents: firstSlot
499
+ };
500
+ let maxSlot = {
501
+ contents: firstSlot
502
+ };
503
+ let requested = new Set();
504
+ blockNumbers.forEach(slot => {
505
+ if (slot < minSlot.contents) {
506
+ minSlot.contents = slot;
507
+ }
508
+ if (slot > maxSlot.contents) {
509
+ maxSlot.contents = slot;
510
+ }
511
+ requested.add(slot);
512
+ });
513
+ let blockDatas = await queryBlockDataRange(minSlot.contents, maxSlot.contents);
514
+ return {
515
+ TAG: "Ok",
516
+ _0: blockDatas.filter(data => requested.delete(data.blockNumber))
517
+ };
518
+ } catch (raw_exn) {
519
+ let exn = Primitive_exceptions.internalToException(raw_exn);
520
+ return {
521
+ TAG: "Error",
522
+ _0: exn
523
+ };
524
+ }
525
+ };
526
+ return {
527
+ name: name,
528
+ sourceFor: "Sync",
529
+ chain: chain,
530
+ poweredByHyperSync: true,
531
+ pollingInterval: 1000,
532
+ getBlockHashes: getBlockHashes,
533
+ getHeightOrThrow: async () => {
534
+ let timer = Hrtime.makeTimer();
535
+ let h = await client.getHeight();
536
+ let seconds = Hrtime.toSecondsFloat(Hrtime.timeSince(timer));
537
+ Prometheus.SourceRequestCount.increment(name, chain, "getHeight");
538
+ Prometheus.SourceRequestCount.addSeconds(name, chain, "getHeight", seconds);
539
+ return h;
540
+ },
541
+ getItemsOrThrow: getItemsOrThrow
542
+ };
543
+ }
544
+
545
+ export {
546
+ buildInstructionSelections,
547
+ synthLogIndex,
548
+ serializeInstructionAddress,
549
+ buildProgramSchemas,
550
+ parseDecoded,
551
+ toSvmInstruction,
552
+ toSvmTransaction,
553
+ toSvmTokenBalance,
554
+ probeRouter,
555
+ make,
556
+ }
557
+ /* Prometheus Not a pure module */
package/src/tui/Tui.res CHANGED
@@ -13,6 +13,7 @@ module ChainLine = {
13
13
  ~endBlock,
14
14
  ~poweredByHyperSync,
15
15
  ~eventsProcessed,
16
+ ~blockUnit: string,
16
17
  ) => {
17
18
  let chainsWidth = Pervasives.min(stdoutColumns - 2, 60)
18
19
  let headerWidth = maxChainIdLength + 10 // 10 for additional text
@@ -27,9 +28,10 @@ module ChainLine = {
27
28
  let toBlockStr = toBlock->TuiData.formatLocaleString
28
29
  let eventsStr = eventsProcessed->TuiData.formatFloatLocaleString
29
30
 
31
+ let endLabel = ` (End ${blockUnit})`
30
32
  let blocksText =
31
- `Blocks: ${progressBlockStr} / ${toBlockStr}` ++
32
- (endBlock->Option.isSome ? " (End Block)" : "") ++ ` `
33
+ `${blockUnit}s: ${progressBlockStr} / ${toBlockStr}` ++
34
+ (endBlock->Option.isSome ? endLabel : "") ++ ` `
33
35
  let eventsText = `Events: ${eventsStr}`
34
36
 
35
37
  let fitsSameLine = blocksText->String.length + eventsText->String.length <= chainsWidth
@@ -213,6 +215,10 @@ module App = {
213
215
  poweredByHyperSync: (
214
216
  cf.sourceManager->SourceManager.getActiveSource
215
217
  ).poweredByHyperSync,
218
+ blockUnit: switch state.ctx.config.ecosystem.name {
219
+ | Svm => "Slot"
220
+ | Evm | Fuel => "Block"
221
+ },
216
222
  rateLimitTimeMs: cf.sourceManager->SourceManager.getRateLimitTimeMs,
217
223
  isRateLimited: cf.sourceManager->SourceManager.isRateLimited,
218
224
  rateLimitResetInMs: cf.sourceManager->SourceManager.getRateLimitResetInMs,
@@ -255,6 +261,7 @@ module App = {
255
261
  stdoutColumns={stdoutColumns}
256
262
  poweredByHyperSync={chainData.poweredByHyperSync}
257
263
  eventsProcessed={chainData.eventsProcessed}
264
+ blockUnit={chainData.blockUnit}
258
265
  />
259
266
  })
260
267
  ->React.array}
@@ -20,6 +20,7 @@ import * as JsxRuntime from "react/jsx-runtime";
20
20
  import * as BufferedProgressBar from "./components/BufferedProgressBar.res.mjs";
21
21
 
22
22
  function Tui$ChainLine(props) {
23
+ let blockUnit = props.blockUnit;
23
24
  let poweredByHyperSync = props.poweredByHyperSync;
24
25
  let endBlock = props.endBlock;
25
26
  let startBlock = props.startBlock;
@@ -34,8 +35,9 @@ function Tui$ChainLine(props) {
34
35
  let progressBlockStr = TuiData.formatLocaleString(progressBlock);
35
36
  let toBlockStr = TuiData.formatLocaleString(toBlock);
36
37
  let eventsStr = TuiData.formatFloatLocaleString(props.eventsProcessed);
37
- let blocksText = `Blocks: ` + progressBlockStr + ` / ` + toBlockStr + (
38
- Stdlib_Option.isSome(endBlock) ? " (End Block)" : ""
38
+ let endLabel = ` (End ` + blockUnit + `)`;
39
+ let blocksText = blockUnit + `s: ` + progressBlockStr + ` / ` + toBlockStr + (
40
+ Stdlib_Option.isSome(endBlock) ? endLabel : ""
39
41
  ) + ` `;
40
42
  let eventsText = `Events: ` + eventsStr;
41
43
  let fitsSameLine = (blocksText.length + eventsText.length | 0) <= chainsWidth;
@@ -243,6 +245,17 @@ function Tui$App(props) {
243
245
  })
244
246
  ) : "SearchingForEvents";
245
247
  }
248
+ let match$1 = state.ctx.config.ecosystem.name;
249
+ let tmp;
250
+ switch (match$1) {
251
+ case "evm" :
252
+ case "fuel" :
253
+ tmp = "Block";
254
+ break;
255
+ case "svm" :
256
+ tmp = "Slot";
257
+ break;
258
+ }
246
259
  return {
247
260
  chainId: cf.chainConfig.id.toString(),
248
261
  eventsProcessed: numEventsProcessed,
@@ -256,6 +269,7 @@ function Tui$App(props) {
256
269
  progress: progress,
257
270
  latestFetchedBlockNumber: latestFetchedBlockNumber,
258
271
  knownHeight: knownHeight,
272
+ blockUnit: tmp,
259
273
  rateLimitTimeMs: SourceManager.getRateLimitTimeMs(cf.sourceManager),
260
274
  isRateLimited: SourceManager.isRateLimited(cf.sourceManager),
261
275
  rateLimitResetInMs: SourceManager.getRateLimitResetInMs(cf.sourceManager)
@@ -339,7 +353,8 @@ function Tui$App(props) {
339
353
  startBlock: chainData.startBlock,
340
354
  endBlock: chainData.endBlock,
341
355
  poweredByHyperSync: chainData.poweredByHyperSync,
342
- eventsProcessed: chainData.eventsProcessed
356
+ eventsProcessed: chainData.eventsProcessed,
357
+ blockUnit: chainData.blockUnit
343
358
  }, i.toString())),
344
359
  JsxRuntime.jsx(Tui$TotalEventsProcessed, {
345
360
  totalEventsProcessed: totalEventsProcessed,
@@ -6,14 +6,14 @@ let make = (~loaded, ~buffered=?, ~outOf, ~barWidth=36, ~loadingColor=Style.Seco
6
6
 
7
7
  let loadedFraction = loaded->Int.toFloat /. outOf->Int.toFloat
8
8
  let loadedCount = Pervasives.min(
9
- Math.floor(maxCount->Int.toFloat *. loadedFraction)->Belt.Float.toInt,
9
+ Math.floor(maxCount->Int.toFloat *. loadedFraction)->Float.toInt,
10
10
  maxCount,
11
11
  )
12
12
 
13
13
  let bufferedCount = buffered->Option.mapOr(loadedCount, buffered => {
14
14
  let bufferedFraction = buffered->Int.toFloat /. outOf->Int.toFloat
15
15
  Pervasives.min(
16
- Math.floor(maxCount->Int.toFloat *. bufferedFraction)->Belt.Float.toInt,
16
+ Math.floor(maxCount->Int.toFloat *. bufferedFraction)->Float.toInt,
17
17
  maxCount,
18
18
  )
19
19
  })
@@ -30,6 +30,9 @@ type chain = {
30
30
  progress: progress,
31
31
  latestFetchedBlockNumber: int,
32
32
  knownHeight: int,
33
+ /** Localized unit noun for this chain's progress. `"Slots"` on SVM,
34
+ `"Blocks"` everywhere else. Drives the per-chain progress label. */
35
+ blockUnit: string,
33
36
  rateLimitTimeMs: float,
34
37
  isRateLimited: bool,
35
38
  rateLimitResetInMs: option<float>,