envio 3.3.0-alpha.0 → 3.3.0-alpha.2

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 (73) hide show
  1. package/package.json +6 -6
  2. package/src/Batch.res +0 -6
  3. package/src/Batch.res.mjs +0 -11
  4. package/src/BatchProcessing.res +2 -12
  5. package/src/BatchProcessing.res.mjs +2 -7
  6. package/src/ChainFetching.res +22 -13
  7. package/src/ChainFetching.res.mjs +13 -13
  8. package/src/ChainMetadata.res +1 -11
  9. package/src/ChainMetadata.res.mjs +1 -10
  10. package/src/ChainState.res +176 -2
  11. package/src/ChainState.res.mjs +153 -9
  12. package/src/ChainState.resi +53 -1
  13. package/src/Config.res +3 -6
  14. package/src/Config.res.mjs +2 -3
  15. package/src/Core.res +7 -0
  16. package/src/CrossChainState.res +99 -91
  17. package/src/CrossChainState.res.mjs +72 -57
  18. package/src/CrossChainState.resi +1 -9
  19. package/src/Ecosystem.res +6 -1
  20. package/src/Env.res +0 -7
  21. package/src/Env.res.mjs +0 -6
  22. package/src/Envio.res +3 -2
  23. package/src/EventConfigBuilder.res +19 -5
  24. package/src/EventConfigBuilder.res.mjs +6 -4
  25. package/src/EventProcessing.res +26 -0
  26. package/src/EventProcessing.res.mjs +20 -0
  27. package/src/FetchState.res +92 -22
  28. package/src/FetchState.res.mjs +87 -14
  29. package/src/IndexerLoop.res +2 -3
  30. package/src/IndexerLoop.res.mjs +1 -1
  31. package/src/IndexerState.res +0 -8
  32. package/src/IndexerState.res.mjs +4 -8
  33. package/src/IndexerState.resi +0 -4
  34. package/src/Internal.res +43 -4
  35. package/src/Internal.res.mjs +18 -0
  36. package/src/Main.res +3 -50
  37. package/src/Main.res.mjs +3 -24
  38. package/src/Prometheus.res +0 -11
  39. package/src/Prometheus.res.mjs +83 -98
  40. package/src/Rollback.res +1 -1
  41. package/src/Rollback.res.mjs +1 -1
  42. package/src/SimulateItems.res +3 -0
  43. package/src/SimulateItems.res.mjs +2 -1
  44. package/src/sources/Evm.res +52 -38
  45. package/src/sources/Evm.res.mjs +48 -36
  46. package/src/sources/Fuel.res +3 -1
  47. package/src/sources/Fuel.res.mjs +1 -0
  48. package/src/sources/HyperFuelSource.res +5 -0
  49. package/src/sources/HyperFuelSource.res.mjs +2 -0
  50. package/src/sources/HyperSync.res +4 -1
  51. package/src/sources/HyperSync.res.mjs +5 -3
  52. package/src/sources/HyperSync.resi +1 -0
  53. package/src/sources/HyperSyncClient.res +9 -78
  54. package/src/sources/HyperSyncClient.res.mjs +1 -22
  55. package/src/sources/HyperSyncSource.res +3 -4
  56. package/src/sources/HyperSyncSource.res.mjs +2 -1
  57. package/src/sources/RpcSource.res +7 -2
  58. package/src/sources/RpcSource.res.mjs +3 -1
  59. package/src/sources/SimulateSource.res +3 -1
  60. package/src/sources/SimulateSource.res.mjs +1 -0
  61. package/src/sources/Source.res +4 -0
  62. package/src/sources/SourceManager.res +9 -8
  63. package/src/sources/SourceManager.res.mjs +21 -26
  64. package/src/sources/SourceManager.resi +2 -3
  65. package/src/sources/Svm.res +25 -2
  66. package/src/sources/Svm.res.mjs +27 -2
  67. package/src/sources/SvmHyperSyncClient.res +3 -29
  68. package/src/sources/SvmHyperSyncSource.res +60 -84
  69. package/src/sources/SvmHyperSyncSource.res.mjs +59 -78
  70. package/src/sources/TransactionStore.res +111 -0
  71. package/src/sources/TransactionStore.res.mjs +77 -0
  72. package/src/tui/Tui.res +13 -16
  73. package/src/tui/Tui.res.mjs +12 -14
@@ -0,0 +1,111 @@
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
+ // Drain another store (a fetch-response page) into this one.
38
+ @send external merge: (t, t) => unit = "merge"
39
+
40
+ // Bulk-materialise the fields selected by `mask` (one bit per field code) for
41
+ // the given transactions, off the JS thread. Result is aligned with the input.
42
+ @send
43
+ external materialize: (
44
+ t,
45
+ ~blockNumbers: array<int>,
46
+ ~transactionIndices: array<int>,
47
+ ~mask: float,
48
+ ) => promise<array<Internal.eventTransaction>> = "materialize"
49
+
50
+ // Drop transactions for blocks at or below the given block (already processed).
51
+ @send external prune: (t, int) => unit = "prune"
52
+
53
+ // Drop transactions for blocks above the given block (rolled back).
54
+ @send external rollback: (t, int) => unit = "rollback"
55
+
56
+ // Materialise the mask-selected fields for the store-backed items and write the
57
+ // resulting transaction onto each item's payload. Items that already carry an
58
+ // inline transaction (RPC/simulate/Fuel) are skipped. Store-backed items always
59
+ // get a transaction object — the selected fields, or `{}` when the chain
60
+ // selected none — so `event.transaction` is never `undefined` (matching the
61
+ // inline sources). Deduped per (blockNumber, transactionIndex).
62
+ let materializeItems = async (store: t, ~items: array<Internal.item>, ~mask: float) => {
63
+ // Store-backed items arrive in (block, logIndex) order, and a transaction's
64
+ // logs are contiguous within a block, so events sharing a (blockNumber,
65
+ // transactionIndex) are adjacent. Group them by extending the current run
66
+ // rather than hashing a string key per item. A key recurring non-adjacently
67
+ // just splits into two groups (one redundant decode) — never incorrect.
68
+ let blockNumbers = []
69
+ let transactionIndices = []
70
+ let payloadGroups = []
71
+
72
+ items->Array.forEach(item =>
73
+ switch item {
74
+ | Internal.Event(_) =>
75
+ let eventItem = item->Internal.castUnsafeEventItem
76
+ switch eventItem.payload->Internal.getPayloadTransaction->Nullable.toOption {
77
+ | Some(_) => () // RPC/simulate/Fuel carry the transaction inline.
78
+ | None =>
79
+ let {blockNumber, transactionIndex} = eventItem
80
+ let last = payloadGroups->Array.length - 1
81
+ if (
82
+ last >= 0 &&
83
+ blockNumbers->Array.getUnsafe(last) == blockNumber &&
84
+ transactionIndices->Array.getUnsafe(last) == transactionIndex
85
+ ) {
86
+ payloadGroups->Array.getUnsafe(last)->Array.push(eventItem.payload)
87
+ } else {
88
+ blockNumbers->Array.push(blockNumber)
89
+ transactionIndices->Array.push(transactionIndex)
90
+ payloadGroups->Array.push([eventItem.payload])
91
+ }
92
+ }
93
+ | Internal.Block(_) => ()
94
+ }
95
+ )
96
+
97
+ if payloadGroups->Utils.Array.notEmpty {
98
+ // A zero mask selects no fields, so there's nothing to decode — each
99
+ // store-backed item still gets an empty transaction object, so
100
+ // `event.transaction` is never undefined (matching the inline sources).
101
+ let txs = if mask == 0. {
102
+ payloadGroups->Array.map((_): Internal.eventTransaction => %raw(`{}`))
103
+ } else {
104
+ await store->materialize(~blockNumbers, ~transactionIndices, ~mask)
105
+ }
106
+ payloadGroups->Array.forEachWithIndex((payloads, i) => {
107
+ let tx = txs->Array.getUnsafe(i)
108
+ payloads->Array.forEach(payload => payload->Internal.setPayloadTransaction(tx))
109
+ })
110
+ }
111
+ }
@@ -0,0 +1,77 @@
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
+ async function materializeItems(store, items, mask) {
36
+ let blockNumbers = [];
37
+ let transactionIndices = [];
38
+ let payloadGroups = [];
39
+ items.forEach(item => {
40
+ if (item.kind !== 0) {
41
+ return;
42
+ }
43
+ let match = item.payload.transaction;
44
+ if (!(match == null)) {
45
+ return;
46
+ }
47
+ let transactionIndex = item.transactionIndex;
48
+ let blockNumber = item.blockNumber;
49
+ let last = payloadGroups.length - 1 | 0;
50
+ if (last >= 0 && blockNumbers[last] === blockNumber && transactionIndices[last] === transactionIndex) {
51
+ payloadGroups[last].push(item.payload);
52
+ } else {
53
+ blockNumbers.push(blockNumber);
54
+ transactionIndices.push(transactionIndex);
55
+ payloadGroups.push([item.payload]);
56
+ }
57
+ });
58
+ if (!Utils.$$Array.notEmpty(payloadGroups)) {
59
+ return;
60
+ }
61
+ let txs = mask === 0 ? payloadGroups.map(param => ({})) : await store.materialize(blockNumbers, transactionIndices, mask);
62
+ payloadGroups.forEach((payloads, i) => {
63
+ let tx = txs[i];
64
+ payloads.forEach(payload => {
65
+ payload.transaction = tx;
66
+ });
67
+ });
68
+ }
69
+
70
+ export {
71
+ make,
72
+ fieldCodes,
73
+ pow2,
74
+ mask,
75
+ materializeItems,
76
+ }
77
+ /* 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),