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.
- package/package.json +6 -6
- package/src/Batch.res +0 -6
- package/src/Batch.res.mjs +0 -11
- package/src/BatchProcessing.res +2 -12
- package/src/BatchProcessing.res.mjs +2 -7
- package/src/ChainFetching.res +22 -13
- package/src/ChainFetching.res.mjs +13 -13
- package/src/ChainMetadata.res +1 -11
- package/src/ChainMetadata.res.mjs +1 -10
- package/src/ChainState.res +176 -2
- package/src/ChainState.res.mjs +153 -9
- package/src/ChainState.resi +53 -1
- package/src/Config.res +3 -6
- package/src/Config.res.mjs +2 -3
- package/src/Core.res +7 -0
- package/src/CrossChainState.res +99 -91
- package/src/CrossChainState.res.mjs +72 -57
- package/src/CrossChainState.resi +1 -9
- package/src/Ecosystem.res +6 -1
- package/src/Env.res +0 -7
- package/src/Env.res.mjs +0 -6
- package/src/Envio.res +3 -2
- package/src/EventConfigBuilder.res +19 -5
- package/src/EventConfigBuilder.res.mjs +6 -4
- package/src/EventProcessing.res +26 -0
- package/src/EventProcessing.res.mjs +20 -0
- package/src/FetchState.res +92 -22
- package/src/FetchState.res.mjs +87 -14
- package/src/IndexerLoop.res +2 -3
- package/src/IndexerLoop.res.mjs +1 -1
- package/src/IndexerState.res +0 -8
- package/src/IndexerState.res.mjs +4 -8
- package/src/IndexerState.resi +0 -4
- package/src/Internal.res +43 -4
- package/src/Internal.res.mjs +18 -0
- package/src/Main.res +3 -50
- package/src/Main.res.mjs +3 -24
- package/src/Prometheus.res +0 -11
- package/src/Prometheus.res.mjs +83 -98
- package/src/Rollback.res +1 -1
- package/src/Rollback.res.mjs +1 -1
- package/src/SimulateItems.res +3 -0
- package/src/SimulateItems.res.mjs +2 -1
- package/src/sources/Evm.res +52 -38
- package/src/sources/Evm.res.mjs +48 -36
- package/src/sources/Fuel.res +3 -1
- package/src/sources/Fuel.res.mjs +1 -0
- package/src/sources/HyperFuelSource.res +5 -0
- package/src/sources/HyperFuelSource.res.mjs +2 -0
- package/src/sources/HyperSync.res +4 -1
- package/src/sources/HyperSync.res.mjs +5 -3
- package/src/sources/HyperSync.resi +1 -0
- package/src/sources/HyperSyncClient.res +9 -78
- package/src/sources/HyperSyncClient.res.mjs +1 -22
- package/src/sources/HyperSyncSource.res +3 -4
- package/src/sources/HyperSyncSource.res.mjs +2 -1
- package/src/sources/RpcSource.res +7 -2
- package/src/sources/RpcSource.res.mjs +3 -1
- package/src/sources/SimulateSource.res +3 -1
- package/src/sources/SimulateSource.res.mjs +1 -0
- package/src/sources/Source.res +4 -0
- package/src/sources/SourceManager.res +9 -8
- package/src/sources/SourceManager.res.mjs +21 -26
- package/src/sources/SourceManager.resi +2 -3
- package/src/sources/Svm.res +25 -2
- package/src/sources/Svm.res.mjs +27 -2
- package/src/sources/SvmHyperSyncClient.res +3 -29
- package/src/sources/SvmHyperSyncSource.res +60 -84
- package/src/sources/SvmHyperSyncSource.res.mjs +59 -78
- package/src/sources/TransactionStore.res +111 -0
- package/src/sources/TransactionStore.res.mjs +77 -0
- package/src/tui/Tui.res +13 -16
- 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
|
|
161
|
-
let
|
|
160
|
+
let data = cs->ChainState.toChainData
|
|
161
|
+
let numEventsProcessed = data.numEventsProcessed
|
|
162
162
|
let committedProgressBlockNumber = cs->ChainState.committedProgressBlockNumber
|
|
163
|
-
let timestampCaughtUpToHeadOrEndblock =
|
|
163
|
+
let timestampCaughtUpToHeadOrEndblock = data.timestampCaughtUpToHeadOrEndblock
|
|
164
164
|
let sourceManager = cs->ChainState.sourceManager
|
|
165
|
-
let latestFetchedBlockNumber =
|
|
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 =
|
|
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 <
|
|
212
|
-
? Some(
|
|
208
|
+
progressBlock: committedProgressBlockNumber < data.startBlock
|
|
209
|
+
? Some(data.startBlock)
|
|
213
210
|
: Some(committedProgressBlockNumber),
|
|
214
211
|
bufferBlock: Some(latestFetchedBlockNumber),
|
|
215
|
-
sourceBlock: Some(
|
|
216
|
-
firstEventBlockNumber:
|
|
217
|
-
startBlock:
|
|
218
|
-
endBlock:
|
|
219
|
-
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"
|
package/src/tui/Tui.res.mjs
CHANGED
|
@@ -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
|
|
212
|
-
let
|
|
210
|
+
let data = ChainState.toChainData(cs);
|
|
211
|
+
let numEventsProcessed = data.numEventsProcessed;
|
|
213
212
|
let committedProgressBlockNumber = ChainState.committedProgressBlockNumber(cs);
|
|
214
|
-
let timestampCaughtUpToHeadOrEndblock =
|
|
213
|
+
let timestampCaughtUpToHeadOrEndblock = data.timestampCaughtUpToHeadOrEndblock;
|
|
215
214
|
let sourceManager = ChainState.sourceManager(cs);
|
|
216
|
-
let latestFetchedBlockNumber =
|
|
215
|
+
let latestFetchedBlockNumber = data.latestFetchedBlockNumber;
|
|
217
216
|
let hasProcessedToEndblock = ChainState.hasProcessedToEndblock(cs);
|
|
218
|
-
let
|
|
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 <
|
|
260
|
+
progressBlock: committedProgressBlockNumber < data.startBlock ? data.startBlock : committedProgressBlockNumber,
|
|
263
261
|
bufferBlock: latestFetchedBlockNumber,
|
|
264
|
-
sourceBlock:
|
|
265
|
-
startBlock:
|
|
266
|
-
endBlock:
|
|
267
|
-
firstEventBlockNumber:
|
|
268
|
-
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:
|
|
269
|
+
knownHeight: data.currentBlockHeight,
|
|
272
270
|
blockUnit: tmp,
|
|
273
271
|
rateLimitTimeMs: SourceManager.getRateLimitTimeMs(sourceManager),
|
|
274
272
|
isRateLimited: SourceManager.isRateLimited(sourceManager),
|