envio 3.3.0-alpha.1 → 3.3.0-alpha.3

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 (84) hide show
  1. package/index.d.ts +23 -21
  2. package/package.json +6 -6
  3. package/src/Batch.res +0 -6
  4. package/src/Batch.res.mjs +0 -11
  5. package/src/BatchProcessing.res +2 -12
  6. package/src/BatchProcessing.res.mjs +2 -7
  7. package/src/ChainFetching.res +42 -6
  8. package/src/ChainFetching.res.mjs +30 -11
  9. package/src/ChainMetadata.res +1 -11
  10. package/src/ChainMetadata.res.mjs +1 -10
  11. package/src/ChainState.res +150 -1
  12. package/src/ChainState.res.mjs +138 -9
  13. package/src/ChainState.resi +51 -1
  14. package/src/Config.res +3 -6
  15. package/src/Config.res.mjs +2 -3
  16. package/src/Core.res +7 -0
  17. package/src/CrossChainState.res +24 -26
  18. package/src/CrossChainState.res.mjs +19 -16
  19. package/src/Ecosystem.res +6 -3
  20. package/src/Envio.res +3 -2
  21. package/src/EventConfigBuilder.res +61 -23
  22. package/src/EventConfigBuilder.res.mjs +27 -13
  23. package/src/EventProcessing.res +63 -21
  24. package/src/EventProcessing.res.mjs +47 -22
  25. package/src/FetchState.res +53 -43
  26. package/src/FetchState.res.mjs +71 -54
  27. package/src/HandlerLoader.res +10 -1
  28. package/src/HandlerLoader.res.mjs +15 -8
  29. package/src/Internal.res +46 -7
  30. package/src/Internal.res.mjs +19 -1
  31. package/src/LoadLayer.res +5 -5
  32. package/src/LoadLayer.res.mjs +6 -6
  33. package/src/Main.res +3 -50
  34. package/src/Main.res.mjs +2 -23
  35. package/src/PgStorage.res +4 -4
  36. package/src/PgStorage.res.mjs +5 -5
  37. package/src/Prometheus.res +10 -10
  38. package/src/Prometheus.res.mjs +9 -9
  39. package/src/PruneStaleHistory.res +2 -2
  40. package/src/PruneStaleHistory.res.mjs +3 -3
  41. package/src/Rollback.res +3 -3
  42. package/src/Rollback.res.mjs +4 -4
  43. package/src/SimulateItems.res +80 -15
  44. package/src/SimulateItems.res.mjs +65 -21
  45. package/src/TestIndexer.res +48 -36
  46. package/src/TestIndexer.res.mjs +35 -28
  47. package/src/bindings/Performance.res +7 -0
  48. package/src/bindings/Performance.res.mjs +21 -0
  49. package/src/bindings/Performance.resi +7 -0
  50. package/src/sources/EventRouter.res +17 -21
  51. package/src/sources/EventRouter.res.mjs +6 -9
  52. package/src/sources/Evm.res +47 -67
  53. package/src/sources/Evm.res.mjs +42 -65
  54. package/src/sources/Fuel.res +3 -3
  55. package/src/sources/Fuel.res.mjs +1 -6
  56. package/src/sources/HyperFuelSource.res +15 -11
  57. package/src/sources/HyperFuelSource.res.mjs +13 -11
  58. package/src/sources/HyperSync.res +7 -1
  59. package/src/sources/HyperSync.res.mjs +6 -3
  60. package/src/sources/HyperSync.resi +2 -0
  61. package/src/sources/HyperSyncClient.res +14 -79
  62. package/src/sources/HyperSyncClient.res.mjs +1 -22
  63. package/src/sources/HyperSyncSource.res +44 -27
  64. package/src/sources/HyperSyncSource.res.mjs +32 -27
  65. package/src/sources/RpcSource.res +14 -10
  66. package/src/sources/RpcSource.res.mjs +11 -9
  67. package/src/sources/SimulateSource.res +4 -2
  68. package/src/sources/SimulateSource.res.mjs +1 -0
  69. package/src/sources/Source.res +5 -1
  70. package/src/sources/SourceManager.res +17 -16
  71. package/src/sources/SourceManager.res.mjs +10 -15
  72. package/src/sources/Svm.res +23 -6
  73. package/src/sources/Svm.res.mjs +24 -6
  74. package/src/sources/SvmHyperSyncClient.res +3 -29
  75. package/src/sources/SvmHyperSyncSource.res +78 -96
  76. package/src/sources/SvmHyperSyncSource.res.mjs +79 -91
  77. package/src/sources/TransactionStore.res +128 -0
  78. package/src/sources/TransactionStore.res.mjs +97 -0
  79. package/src/tui/Tui.res +13 -16
  80. package/src/tui/Tui.res.mjs +12 -14
  81. package/svm.schema.json +30 -37
  82. package/src/bindings/Hrtime.res +0 -58
  83. package/src/bindings/Hrtime.res.mjs +0 -90
  84. package/src/bindings/Hrtime.resi +0 -30
@@ -1,9 +1,9 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
- import * as Hrtime from "../bindings/Hrtime.res.mjs";
4
3
  import * as Source from "./Source.res.mjs";
5
4
  import * as Prometheus from "../Prometheus.res.mjs";
6
5
  import * as EventRouter from "./EventRouter.res.mjs";
6
+ import * as Performance from "../bindings/Performance.res.mjs";
7
7
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
8
8
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
9
9
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
@@ -166,7 +166,7 @@ function parseDecoded(d) {
166
166
  };
167
167
  }
168
168
 
169
- function toSvmInstruction(instr, programName, instructionName, transaction, logs, block) {
169
+ function toSvmInstruction(instr, programName, instructionName, logs, block) {
170
170
  return {
171
171
  programName: programName,
172
172
  instructionName: instructionName,
@@ -180,40 +180,15 @@ function toSvmInstruction(instr, programName, instructionName, transaction, logs
180
180
  d4: instr.d4,
181
181
  d8: instr.d8,
182
182
  params: Stdlib_Option.map(instr.decoded, parseDecoded),
183
- transaction: transaction,
184
183
  logs: logs,
185
184
  block: block
186
185
  };
187
186
  }
188
187
 
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) {
188
+ function probeRouter(router, programId, instr, byteLengthsDesc, contractAddress, contractNameByAddress) {
214
189
  let probe = dN => {
215
190
  let tag = EventRouter.getSvmEventId(programId, dN);
216
- return EventRouter.get(router, tag, contractAddress, instr.slot, indexingAddresses);
191
+ return EventRouter.get(router, tag, contractAddress, contractNameByAddress);
217
192
  };
218
193
  let result = Stdlib_Array.reduce(byteLengthsDesc, undefined, (acc, len) => {
219
194
  if (acc !== undefined) {
@@ -247,6 +222,32 @@ function probeRouter(router, programId, instr, byteLengthsDesc, contractAddress,
247
222
  }
248
223
  }
249
224
 
225
+ function toQueryTxField(field) {
226
+ switch (field) {
227
+ case "signatures" :
228
+ return "signatures";
229
+ case "feePayer" :
230
+ return "fee_payer";
231
+ case "success" :
232
+ return "success";
233
+ case "err" :
234
+ return "err";
235
+ case "fee" :
236
+ return "fee";
237
+ case "computeUnitsConsumed" :
238
+ return "compute_units_consumed";
239
+ case "accountKeys" :
240
+ return "account_keys";
241
+ case "recentBlockhash" :
242
+ return "recent_blockhash";
243
+ case "version" :
244
+ return "version";
245
+ case "transactionIndex" :
246
+ case "tokenBalances" :
247
+ return;
248
+ }
249
+ }
250
+
250
251
  function make(param) {
251
252
  let eventConfigs = param.eventConfigs;
252
253
  let chain = param.chain;
@@ -259,31 +260,44 @@ function make(param) {
259
260
  match[1].forEach(o => {
260
261
  orderingByProgram[o.programId] = o.byteLengthsDesc;
261
262
  });
262
- let needsTransactions = eventConfigs.some(cfg => cfg.includeTransaction);
263
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();
264
+ let selectedTxFields = new Set();
265
+ eventConfigs.forEach(cfg => {
266
+ cfg.selectedTransactionFields.forEach(field => {
267
+ selectedTxFields.add(field);
268
+ });
269
+ });
270
+ let needsTokenBalances = selectedTxFields.has("tokenBalances");
271
+ let fields = [
272
+ "slot",
273
+ "transaction_index"
274
+ ];
275
+ Array.from(selectedTxFields).forEach(field => {
276
+ let queryField = toQueryTxField(field);
277
+ if (queryField !== undefined) {
278
+ fields.push(queryField);
279
+ return;
280
+ }
281
+ });
282
+ let needsTransactions = Array.from(selectedTxFields).some(field => {
283
+ switch (field) {
284
+ case "transactionIndex" :
285
+ case "tokenBalances" :
286
+ return false;
287
+ default:
288
+ return true;
289
+ }
290
+ });
291
+ let getItemsOrThrow = async (fromBlock, toBlock, param, contractNameByAddress, knownHeight, param$1, param$2, retry, logger) => {
292
+ let totalTimeRef = Performance.now();
293
+ let pageFetchRef = Performance.now();
268
294
  let instructionSelections = buildInstructionSelections(eventConfigs);
269
295
  let fields_block = [
270
296
  "slot",
271
297
  "blockhash",
272
298
  "block_time"
273
299
  ];
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;
300
+ let fields_transaction = needsTransactions ? fields : undefined;
287
301
  let fields_log = needsLogs ? [
288
302
  "slot",
289
303
  "transaction_index",
@@ -300,7 +314,7 @@ function make(param) {
300
314
  "pre_amount",
301
315
  "post_amount"
302
316
  ] : undefined;
303
- let fields = {
317
+ let fields$1 = {
304
318
  block: fields_block,
305
319
  transaction: fields_transaction,
306
320
  log: fields_log,
@@ -308,7 +322,7 @@ function make(param) {
308
322
  };
309
323
  let query_toSlot = Stdlib_Option.map(toBlock, toBlock => toBlock + 1 | 0);
310
324
  let query_instructions = instructionSelections;
311
- let query_fields = fields;
325
+ let query_fields = fields$1;
312
326
  let query = {
313
327
  fromSlot: fromBlock,
314
328
  toSlot: query_toSlot,
@@ -316,9 +330,9 @@ function make(param) {
316
330
  fields: query_fields
317
331
  };
318
332
  Prometheus.SourceRequestCount.increment(name, chain, "getInstructions");
319
- let resp;
333
+ let match;
320
334
  try {
321
- resp = await client.get(query);
335
+ match = await client.get(query);
322
336
  } catch (raw_exn) {
323
337
  let exn = Primitive_exceptions.internalToException(raw_exn);
324
338
  throw {
@@ -336,8 +350,9 @@ function make(param) {
336
350
  Error: new Error()
337
351
  };
338
352
  }
339
- let pageFetchTime = Hrtime.toSecondsFloat(Hrtime.timeSince(pageFetchRef));
340
- let parsingRef = Hrtime.makeTimer();
353
+ let resp = match[0];
354
+ let pageFetchTime = Performance.secondsSince(pageFetchRef);
355
+ let parsingRef = Performance.now();
341
356
  let blockTimeBySlot = {};
342
357
  resp.data.blocks.forEach(b => {
343
358
  let t = b.blockTime;
@@ -346,11 +361,6 @@ function make(param) {
346
361
  return;
347
362
  }
348
363
  });
349
- let txByKey = {};
350
- resp.data.transactions.forEach(tx => {
351
- let key = tx.slot.toString() + ":" + tx.transactionIndex.toString();
352
- txByKey[key] = tx;
353
- });
354
364
  let logsByKey = {};
355
365
  resp.data.logs.forEach(log => {
356
366
  let match = log.transactionIndex;
@@ -369,37 +379,13 @@ function make(param) {
369
379
  logsByKey[key] = [log];
370
380
  }
371
381
  });
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
382
  let parsedQueueItems = [];
389
383
  resp.data.instructions.forEach(instr => {
390
384
  let programId = instr.programId;
391
385
  let byteLengths = Stdlib_Option.getOr(orderingByProgram[instr.programId], []);
392
386
  let contractAddress = instr.programId;
393
- let maybeConfig = probeRouter(eventRouter, programId, instr, byteLengths, contractAddress, indexingAddresses);
387
+ let maybeConfig = probeRouter(eventRouter, programId, instr, byteLengths, contractAddress, contractNameByAddress);
394
388
  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
389
  let logKey = instr.slot.toString() + ":" + instr.transactionIndex.toString() + ":" + serializeInstructionAddress(instr.instructionAddress);
404
390
  let maybeLogs = Stdlib_Option.map(logsByKey[logKey], logs => logs.map(log => ({
405
391
  kind: Stdlib_Option.getOr(log.kind, ""),
@@ -407,7 +393,7 @@ function make(param) {
407
393
  })));
408
394
  let slotKey = instr.slot.toString();
409
395
  let blockTime = blockTimeBySlot[slotKey];
410
- let payload = toSvmInstruction(instr, maybeConfig.contractName, maybeConfig.name, maybeConfig.includeTransaction ? maybeTx$1 : undefined, maybeConfig.includeLogs ? maybeLogs : undefined, {
396
+ let payload = toSvmInstruction(instr, maybeConfig.contractName, maybeConfig.name, maybeConfig.includeLogs ? maybeLogs : undefined, {
411
397
  slot: instr.slot,
412
398
  time: Stdlib_Option.getOr(blockTime, 0),
413
399
  hash: ""
@@ -420,22 +406,24 @@ function make(param) {
420
406
  blockNumber: instr.slot,
421
407
  blockHash: "",
422
408
  logIndex: synthLogIndex(instr),
409
+ transactionIndex: instr.transactionIndex,
423
410
  payload: payload
424
411
  });
425
412
  }
426
413
  });
427
- let parsingTimeElapsed = Hrtime.toSecondsFloat(Hrtime.timeSince(parsingRef));
414
+ let parsingTimeElapsed = Performance.secondsSince(parsingRef);
428
415
  let highestSlot = resp.nextSlot - 1 | 0;
429
416
  let latestBlockTime = Stdlib_Option.getOr(blockTimeBySlot[highestSlot.toString()], 0);
430
417
  let blockHashes = resp.data.blocks.map(b => ({
431
418
  blockHash: b.blockhash,
432
419
  blockNumber: b.slot
433
420
  }));
434
- let totalTimeElapsed = Hrtime.toSecondsFloat(Hrtime.timeSince(totalTimeRef));
421
+ let totalTimeElapsed = Performance.secondsSince(totalTimeRef);
435
422
  return {
436
423
  knownHeight: knownHeight,
437
424
  blockHashes: blockHashes,
438
425
  parsedQueueItems: parsedQueueItems,
426
+ transactionStore: Primitive_option.some(match[1]),
439
427
  fromBlockQueried: fromBlock,
440
428
  latestFetchedBlockNumber: highestSlot,
441
429
  latestFetchedBlockTimestamp: latestBlockTime,
@@ -469,7 +457,8 @@ function make(param) {
469
457
  maxNumBlocks: query_maxNumBlocks
470
458
  };
471
459
  Prometheus.SourceRequestCount.increment(name, chain, "getBlockHashes");
472
- let resp = await client.get(query);
460
+ let match = await client.get(query);
461
+ let resp = match[0];
473
462
  resp.data.blocks.forEach(b => {
474
463
  blockDatas.push({
475
464
  blockHash: b.blockhash,
@@ -531,9 +520,9 @@ function make(param) {
531
520
  pollingInterval: 1000,
532
521
  getBlockHashes: getBlockHashes,
533
522
  getHeightOrThrow: async () => {
534
- let timer = Hrtime.makeTimer();
523
+ let timer = Performance.now();
535
524
  let h = await client.getHeight();
536
- let seconds = Hrtime.toSecondsFloat(Hrtime.timeSince(timer));
525
+ let seconds = Performance.secondsSince(timer);
537
526
  Prometheus.SourceRequestCount.increment(name, chain, "getHeight");
538
527
  Prometheus.SourceRequestCount.addSeconds(name, chain, "getHeight", seconds);
539
528
  return h;
@@ -549,9 +538,8 @@ export {
549
538
  buildProgramSchemas,
550
539
  parseDecoded,
551
540
  toSvmInstruction,
552
- toSvmTransaction,
553
- toSvmTokenBalance,
554
541
  probeRouter,
542
+ toQueryTxField,
555
543
  make,
556
544
  }
557
545
  /* Prometheus Not a pure module */
@@ -0,0 +1,128 @@
1
+ // Binding to the Rust `TransactionStore` napi class. Transactions are kept in
2
+ // Rust as raw structs (their large fields never enter JS until read) keyed by
3
+ // (blockNumber, transactionIndex). One store lives per chain on `ChainState`;
4
+ // each fetch response contributes a page that is merged in. At batch
5
+ // preparation the selected fields are materialised in bulk, off the JS thread,
6
+ // in columnar form and zipped into plain JS objects on the main thread.
7
+ type t
8
+
9
+ @send external classNew: Core.transactionStoreCtor => t = "new"
10
+ let make = (): t => Core.getAddon().transactionStore->classNew
11
+
12
+ // Field-name → bit-index map from an ordered field-name array. The index is the
13
+ // field code shared with the Rust store (`EvmTxField`/`SvmTxField`).
14
+ let fieldCodes = (fields: array<string>): dict<int> => {
15
+ let codes = Dict.make()
16
+ fields->Array.forEachWithIndex((name, i) => codes->Dict.set(name, i))
17
+ codes
18
+ }
19
+
20
+ let pow2: int => float = %raw(`c => Math.pow(2, c)`)
21
+
22
+ // Union of an ecosystem's selected transaction fields as a bitmask float (bit
23
+ // `code` set ⇔ selected). Built arithmetically to dodge 32-bit JS bitwise ops.
24
+ let mask = (eventConfigs: array<Internal.eventConfig>, ~codes: dict<int>): float => {
25
+ let selected = Utils.Set.make()
26
+ eventConfigs->Array.forEach(eventConfig =>
27
+ eventConfig.selectedTransactionFields->Utils.Set.forEach(name =>
28
+ switch codes->Utils.Dict.dangerouslyGetNonOption(name) {
29
+ | Some(code) => selected->Utils.Set.add(code)->ignore
30
+ | None => ()
31
+ }
32
+ )
33
+ )
34
+ selected->Utils.Set.toArray->Array.reduce(0., (mask, code) => mask +. pow2(code))
35
+ }
36
+
37
+ // Build an ecosystem's mask function from its ordered field-name array. The
38
+ // field codes are derived once and closed over.
39
+ let makeMaskFn = (fields: array<string>): (array<Internal.eventConfig> => float) => {
40
+ let codes = fieldCodes(fields)
41
+ eventConfigs => eventConfigs->mask(~codes)
42
+ }
43
+
44
+ // Drain another store (a fetch-response page) into this one.
45
+ @send external merge: (t, t) => unit = "merge"
46
+
47
+ // Bulk-materialise the fields selected by `mask` (one bit per field code) for
48
+ // the given transactions, off the JS thread. Result is aligned with the input.
49
+ @send
50
+ external materialize: (
51
+ t,
52
+ ~blockNumbers: array<int>,
53
+ ~transactionIndices: array<int>,
54
+ ~mask: float,
55
+ ) => promise<array<Internal.eventTransaction>> = "materialize"
56
+
57
+ // Drop transactions for blocks at or below the given block (already processed).
58
+ @send external prune: (t, int) => unit = "prune"
59
+
60
+ // Drop transactions for blocks above the given block (rolled back).
61
+ @send external rollback: (t, int) => unit = "rollback"
62
+
63
+ // Materialise the mask-selected fields for the store-backed items and write the
64
+ // resulting transaction onto each item's payload. Items that already carry an
65
+ // inline transaction (RPC/simulate/Fuel) are skipped. Store-backed items always
66
+ // get a transaction object — the selected fields, or `{}` when the chain
67
+ // selected none — so `event.transaction` is never `undefined` (matching the
68
+ // inline sources). Deduped per (blockNumber, transactionIndex).
69
+ let materializeItems = async (store: t, ~items: array<Internal.item>, ~mask: float) => {
70
+ if mask == 0. {
71
+ // No fields selected: there's nothing to decode and no reason to group by
72
+ // key, but each store-backed item still gets an empty transaction object so
73
+ // `event.transaction` is never undefined (matching the inline sources).
74
+ items->Array.forEach(item =>
75
+ switch item {
76
+ | Internal.Event(_) =>
77
+ let payload = (item->Internal.castUnsafeEventItem).payload
78
+ switch payload->Internal.getPayloadTransaction->Nullable.toOption {
79
+ | Some(_) => () // RPC/simulate/Fuel carry the transaction inline.
80
+ | None => payload->Internal.setPayloadTransaction(%raw(`{}`))
81
+ }
82
+ | Internal.Block(_) => ()
83
+ }
84
+ )
85
+ } else {
86
+ // Store-backed items arrive in (block, logIndex) order, and a transaction's
87
+ // logs are contiguous within a block, so events sharing a (blockNumber,
88
+ // transactionIndex) are adjacent. Group them by extending the current run
89
+ // rather than hashing a string key per item. A key recurring non-adjacently
90
+ // just splits into two groups (one redundant decode) — never incorrect.
91
+ let blockNumbers = []
92
+ let transactionIndices = []
93
+ let payloadGroups = []
94
+
95
+ items->Array.forEach(item =>
96
+ switch item {
97
+ | Internal.Event(_) =>
98
+ let eventItem = item->Internal.castUnsafeEventItem
99
+ switch eventItem.payload->Internal.getPayloadTransaction->Nullable.toOption {
100
+ | Some(_) => () // RPC/simulate/Fuel carry the transaction inline.
101
+ | None =>
102
+ let {blockNumber, transactionIndex} = eventItem
103
+ let last = payloadGroups->Array.length - 1
104
+ if (
105
+ last >= 0 &&
106
+ blockNumbers->Array.getUnsafe(last) == blockNumber &&
107
+ transactionIndices->Array.getUnsafe(last) == transactionIndex
108
+ ) {
109
+ payloadGroups->Array.getUnsafe(last)->Array.push(eventItem.payload)
110
+ } else {
111
+ blockNumbers->Array.push(blockNumber)
112
+ transactionIndices->Array.push(transactionIndex)
113
+ payloadGroups->Array.push([eventItem.payload])
114
+ }
115
+ }
116
+ | Internal.Block(_) => ()
117
+ }
118
+ )
119
+
120
+ if payloadGroups->Utils.Array.notEmpty {
121
+ let txs = await store->materialize(~blockNumbers, ~transactionIndices, ~mask)
122
+ payloadGroups->Array.forEachWithIndex((payloads, i) => {
123
+ let tx = txs->Array.getUnsafe(i)
124
+ payloads->Array.forEach(payload => payload->Internal.setPayloadTransaction(tx))
125
+ })
126
+ }
127
+ }
128
+ }
@@ -0,0 +1,97 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Core from "../Core.res.mjs";
4
+ import * as Utils from "../Utils.res.mjs";
5
+ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
6
+
7
+ function make() {
8
+ return Core.getAddon().TransactionStore.new();
9
+ }
10
+
11
+ function fieldCodes(fields) {
12
+ let codes = {};
13
+ fields.forEach((name, i) => {
14
+ codes[name] = i;
15
+ });
16
+ return codes;
17
+ }
18
+
19
+ let pow2 = (c => Math.pow(2, c));
20
+
21
+ function mask(eventConfigs, codes) {
22
+ let selected = new Set();
23
+ eventConfigs.forEach(eventConfig => {
24
+ eventConfig.selectedTransactionFields.forEach(name => {
25
+ let code = codes[name];
26
+ if (code !== undefined) {
27
+ selected.add(code);
28
+ return;
29
+ }
30
+ });
31
+ });
32
+ return Stdlib_Array.reduce(Array.from(selected), 0, (mask, code) => mask + pow2(code));
33
+ }
34
+
35
+ function makeMaskFn(fields) {
36
+ let codes = fieldCodes(fields);
37
+ return eventConfigs => mask(eventConfigs, codes);
38
+ }
39
+
40
+ async function materializeItems(store, items, mask) {
41
+ if (mask === 0) {
42
+ items.forEach(item => {
43
+ if (item.kind !== 0) {
44
+ return;
45
+ }
46
+ let payload = item.payload;
47
+ let match = payload.transaction;
48
+ if (match == null) {
49
+ payload.transaction = {};
50
+ return;
51
+ }
52
+ });
53
+ return;
54
+ }
55
+ let blockNumbers = [];
56
+ let transactionIndices = [];
57
+ let payloadGroups = [];
58
+ items.forEach(item => {
59
+ if (item.kind !== 0) {
60
+ return;
61
+ }
62
+ let match = item.payload.transaction;
63
+ if (!(match == null)) {
64
+ return;
65
+ }
66
+ let transactionIndex = item.transactionIndex;
67
+ let blockNumber = item.blockNumber;
68
+ let last = payloadGroups.length - 1 | 0;
69
+ if (last >= 0 && blockNumbers[last] === blockNumber && transactionIndices[last] === transactionIndex) {
70
+ payloadGroups[last].push(item.payload);
71
+ } else {
72
+ blockNumbers.push(blockNumber);
73
+ transactionIndices.push(transactionIndex);
74
+ payloadGroups.push([item.payload]);
75
+ }
76
+ });
77
+ if (!Utils.$$Array.notEmpty(payloadGroups)) {
78
+ return;
79
+ }
80
+ let txs = await store.materialize(blockNumbers, transactionIndices, mask);
81
+ payloadGroups.forEach((payloads, i) => {
82
+ let tx = txs[i];
83
+ payloads.forEach(payload => {
84
+ payload.transaction = tx;
85
+ });
86
+ });
87
+ }
88
+
89
+ export {
90
+ make,
91
+ fieldCodes,
92
+ pow2,
93
+ mask,
94
+ makeMaskFn,
95
+ materializeItems,
96
+ }
97
+ /* Core Not a pure module */
package/src/tui/Tui.res CHANGED
@@ -157,18 +157,15 @@ module App = {
157
157
  ->IndexerState.chainStates
158
158
  ->Dict.valuesToArray
159
159
  ->Array.map(cs => {
160
- let numEventsProcessed = cs->ChainState.numEventsProcessed
161
- let fetchState = cs->ChainState.fetchState
160
+ let data = cs->ChainState.toChainData
161
+ let numEventsProcessed = data.numEventsProcessed
162
162
  let committedProgressBlockNumber = cs->ChainState.committedProgressBlockNumber
163
- let timestampCaughtUpToHeadOrEndblock = cs->ChainState.timestampCaughtUpToHeadOrEndblock
163
+ let timestampCaughtUpToHeadOrEndblock = data.timestampCaughtUpToHeadOrEndblock
164
164
  let sourceManager = cs->ChainState.sourceManager
165
- let latestFetchedBlockNumber = Pervasives.max(fetchState->FetchState.bufferBlockNumber, 0)
165
+ let latestFetchedBlockNumber = data.latestFetchedBlockNumber
166
166
  let hasProcessedToEndblock = cs->ChainState.hasProcessedToEndblock
167
- let knownHeight = hasProcessedToEndblock
168
- ? fetchState.endBlock->Option.getOr(fetchState.knownHeight)
169
- : fetchState.knownHeight
170
167
 
171
- let firstEventBlock = fetchState.firstEventBlock
168
+ let firstEventBlock = data.firstEventBlockNumber
172
169
  let progress: TuiData.progress = if hasProcessedToEndblock {
173
170
  // If the endblock has been reached then set the progress to synced.
174
171
  // if there's chains that have no events in the block range start->end,
@@ -204,19 +201,19 @@ module App = {
204
201
  (
205
202
  {
206
203
  progress,
207
- knownHeight,
204
+ knownHeight: data.knownHeight,
208
205
  latestFetchedBlockNumber,
209
206
  eventsProcessed: numEventsProcessed,
210
207
  chainId: (cs->ChainState.chainConfig).id->Int.toString,
211
- progressBlock: committedProgressBlockNumber < fetchState.startBlock
212
- ? Some(fetchState.startBlock)
208
+ progressBlock: committedProgressBlockNumber < data.startBlock
209
+ ? Some(data.startBlock)
213
210
  : Some(committedProgressBlockNumber),
214
211
  bufferBlock: Some(latestFetchedBlockNumber),
215
- sourceBlock: Some(fetchState.knownHeight),
216
- firstEventBlockNumber: fetchState.firstEventBlock,
217
- startBlock: fetchState.startBlock,
218
- endBlock: fetchState.endBlock,
219
- poweredByHyperSync: (sourceManager->SourceManager.getActiveSource).poweredByHyperSync,
212
+ sourceBlock: Some(cs->ChainState.knownHeight),
213
+ firstEventBlockNumber: firstEventBlock,
214
+ startBlock: data.startBlock,
215
+ endBlock: data.endBlock,
216
+ poweredByHyperSync: data.poweredByHyperSync,
220
217
  blockUnit: switch (state->IndexerState.config).ecosystem.name {
221
218
  | Svm => "Slot"
222
219
  | Evm | Fuel => "Block"
@@ -8,7 +8,6 @@ import * as SyncETA from "./components/SyncETA.res.mjs";
8
8
  import * as TuiData from "./components/TuiData.res.mjs";
9
9
  import * as Messages from "./components/Messages.res.mjs";
10
10
  import * as ChainState from "../ChainState.res.mjs";
11
- import * as FetchState from "../FetchState.res.mjs";
12
11
  import * as IndexerState from "../IndexerState.res.mjs";
13
12
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
14
13
  import InkBigText from "ink-big-text";
@@ -208,15 +207,14 @@ function Tui$App(props) {
208
207
  };
209
208
  }, [getState]);
210
209
  let chains = Object.values(IndexerState.chainStates(state)).map(cs => {
211
- let numEventsProcessed = ChainState.numEventsProcessed(cs);
212
- let fetchState = ChainState.fetchState(cs);
210
+ let data = ChainState.toChainData(cs);
211
+ let numEventsProcessed = data.numEventsProcessed;
213
212
  let committedProgressBlockNumber = ChainState.committedProgressBlockNumber(cs);
214
- let timestampCaughtUpToHeadOrEndblock = ChainState.timestampCaughtUpToHeadOrEndblock(cs);
213
+ let timestampCaughtUpToHeadOrEndblock = data.timestampCaughtUpToHeadOrEndblock;
215
214
  let sourceManager = ChainState.sourceManager(cs);
216
- let latestFetchedBlockNumber = Primitive_int.max(FetchState.bufferBlockNumber(fetchState), 0);
215
+ let latestFetchedBlockNumber = data.latestFetchedBlockNumber;
217
216
  let hasProcessedToEndblock = ChainState.hasProcessedToEndblock(cs);
218
- let knownHeight = hasProcessedToEndblock ? Stdlib_Option.getOr(fetchState.endBlock, fetchState.knownHeight) : fetchState.knownHeight;
219
- let firstEventBlock = fetchState.firstEventBlock;
217
+ let firstEventBlock = data.firstEventBlockNumber;
220
218
  let progress = hasProcessedToEndblock ? ({
221
219
  TAG: "Synced",
222
220
  _0: {
@@ -259,16 +257,16 @@ function Tui$App(props) {
259
257
  return {
260
258
  chainId: ChainState.chainConfig(cs).id.toString(),
261
259
  eventsProcessed: numEventsProcessed,
262
- progressBlock: committedProgressBlockNumber < fetchState.startBlock ? fetchState.startBlock : committedProgressBlockNumber,
260
+ progressBlock: committedProgressBlockNumber < data.startBlock ? data.startBlock : committedProgressBlockNumber,
263
261
  bufferBlock: latestFetchedBlockNumber,
264
- sourceBlock: fetchState.knownHeight,
265
- startBlock: fetchState.startBlock,
266
- endBlock: fetchState.endBlock,
267
- firstEventBlockNumber: fetchState.firstEventBlock,
268
- poweredByHyperSync: SourceManager.getActiveSource(sourceManager).poweredByHyperSync,
262
+ sourceBlock: ChainState.knownHeight(cs),
263
+ startBlock: data.startBlock,
264
+ endBlock: data.endBlock,
265
+ firstEventBlockNumber: firstEventBlock,
266
+ poweredByHyperSync: data.poweredByHyperSync,
269
267
  progress: progress,
270
268
  latestFetchedBlockNumber: latestFetchedBlockNumber,
271
- knownHeight: knownHeight,
269
+ knownHeight: data.currentBlockHeight,
272
270
  blockUnit: tmp,
273
271
  rateLimitTimeMs: SourceManager.getRateLimitTimeMs(sourceManager),
274
272
  isRateLimited: SourceManager.isRateLimited(sourceManager),