envio 3.3.0-alpha.3 → 3.3.0-alpha.5
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/evm.schema.json +10 -0
- package/package.json +7 -7
- package/src/Batch.res.mjs +1 -1
- package/src/Bin.res +3 -0
- package/src/Bin.res.mjs +4 -0
- package/src/ChainFetching.res +1 -3
- package/src/ChainFetching.res.mjs +3 -3
- package/src/ChainState.res +44 -19
- package/src/ChainState.res.mjs +25 -23
- package/src/ChainState.resi +3 -3
- package/src/Config.res +3 -0
- package/src/Config.res.mjs +3 -1
- package/src/Core.res +0 -3
- package/src/Ecosystem.res +0 -4
- package/src/EventConfigBuilder.res +3 -0
- package/src/EventConfigBuilder.res.mjs +7 -1
- package/src/FetchState.res +73 -160
- package/src/FetchState.res.mjs +120 -222
- package/src/IndexingAddresses.res +105 -0
- package/src/IndexingAddresses.res.mjs +100 -0
- package/src/IndexingAddresses.resi +32 -0
- package/src/Internal.res +5 -0
- package/src/Main.res +21 -24
- package/src/Main.res.mjs +21 -20
- package/src/Metrics.res +33 -0
- package/src/Metrics.res.mjs +39 -0
- package/src/Prometheus.res +2 -20
- package/src/Prometheus.res.mjs +83 -95
- package/src/SimulateItems.res +4 -0
- package/src/TestIndexer.res +43 -17
- package/src/TestIndexer.res.mjs +16 -9
- package/src/bindings/Viem.res +0 -41
- package/src/bindings/Viem.res.mjs +1 -43
- package/src/sources/Evm.res +17 -39
- package/src/sources/Evm.res.mjs +8 -40
- package/src/sources/EvmChain.res +3 -1
- package/src/sources/EvmChain.res.mjs +2 -1
- package/src/sources/EvmRpcClient.res +36 -5
- package/src/sources/EvmRpcClient.res.mjs +7 -4
- package/src/sources/Fuel.res +0 -2
- package/src/sources/Fuel.res.mjs +0 -1
- package/src/sources/HyperSyncClient.res +0 -35
- package/src/sources/HyperSyncClient.res.mjs +1 -8
- package/src/sources/Rpc.res +15 -47
- package/src/sources/Rpc.res.mjs +25 -56
- package/src/sources/RpcSource.res +30 -73
- package/src/sources/RpcSource.res.mjs +24 -73
- package/src/sources/SourceManager.res +1 -1
- package/src/sources/SourceManager.res.mjs +1 -1
- package/src/sources/Svm.res +10 -17
- package/src/sources/Svm.res.mjs +6 -18
- package/src/sources/TransactionStore.res +98 -76
- package/src/sources/TransactionStore.res.mjs +49 -35
|
@@ -6,8 +6,20 @@
|
|
|
6
6
|
// in columnar form and zipped into plain JS objects on the main thread.
|
|
7
7
|
type t
|
|
8
8
|
|
|
9
|
-
@send external
|
|
10
|
-
|
|
9
|
+
@send external newEvm: (Core.transactionStoreCtor, ~shouldChecksum: bool) => t = "newEvm"
|
|
10
|
+
@send external newSvm: Core.transactionStoreCtor => t = "newSvm"
|
|
11
|
+
@send external newFuel: Core.transactionStoreCtor => t = "newFuel"
|
|
12
|
+
|
|
13
|
+
// The store's ecosystem is fixed here, from the chain's config. EVM carries the
|
|
14
|
+
// chain's address-checksumming setting; SVM/Fuel need no extra data.
|
|
15
|
+
let make = (~ecosystem: Ecosystem.name, ~shouldChecksum: bool): t => {
|
|
16
|
+
let ctor = Core.getAddon().transactionStore
|
|
17
|
+
switch ecosystem {
|
|
18
|
+
| Evm => ctor->newEvm(~shouldChecksum)
|
|
19
|
+
| Svm => ctor->newSvm
|
|
20
|
+
| Fuel => ctor->newFuel
|
|
21
|
+
}
|
|
22
|
+
}
|
|
11
23
|
|
|
12
24
|
// Field-name → bit-index map from an ordered field-name array. The index is the
|
|
13
25
|
// field code shared with the Rust store (`EvmTxField`/`SvmTxField`).
|
|
@@ -19,39 +31,45 @@ let fieldCodes = (fields: array<string>): dict<int> => {
|
|
|
19
31
|
|
|
20
32
|
let pow2: int => float = %raw(`c => Math.pow(2, c)`)
|
|
21
33
|
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
// One event's selected transaction fields as a bitmask float (bit `code` set ⇔
|
|
35
|
+
// selected). Each field appears once, so summing `pow2(code)` sets distinct bits
|
|
36
|
+
// with no overlap; the result stays exact in f64 (codes span 0..31, so a mask is
|
|
37
|
+
// at most 2^32-1).
|
|
38
|
+
let maskFromFields = (selectedTransactionFields: Utils.Set.t<string>, ~codes: dict<int>): float => {
|
|
39
|
+
let mask = ref(0.)
|
|
40
|
+
selectedTransactionFields->Utils.Set.forEach(name =>
|
|
41
|
+
switch codes->Utils.Dict.dangerouslyGetNonOption(name) {
|
|
42
|
+
| Some(code) => mask := mask.contents +. pow2(code)
|
|
43
|
+
| None => ()
|
|
44
|
+
}
|
|
33
45
|
)
|
|
34
|
-
|
|
46
|
+
mask.contents
|
|
35
47
|
}
|
|
36
48
|
|
|
37
|
-
// Build an ecosystem's mask function from its ordered field-name
|
|
38
|
-
// field codes are derived once and closed over.
|
|
39
|
-
let makeMaskFn = (fields: array<string>): (
|
|
49
|
+
// Build an ecosystem's per-event mask function from its ordered field-name
|
|
50
|
+
// array. The field codes are derived once and closed over.
|
|
51
|
+
let makeMaskFn = (fields: array<string>): (Utils.Set.t<string> => float) => {
|
|
40
52
|
let codes = fieldCodes(fields)
|
|
41
|
-
|
|
53
|
+
selectedTransactionFields => selectedTransactionFields->maskFromFields(~codes)
|
|
42
54
|
}
|
|
43
55
|
|
|
56
|
+
// Bitwise OR of two per-event masks. Masks fit in 32 bits (≤32 transaction
|
|
57
|
+
// fields), so `>>> 0` recovers the unsigned value that a plain `|` renders
|
|
58
|
+
// negative once bit 31 is set.
|
|
59
|
+
let orMask: (float, float) => float = %raw(`(a, b) => (a | b) >>> 0`)
|
|
60
|
+
|
|
44
61
|
// Drain another store (a fetch-response page) into this one.
|
|
45
62
|
@send external merge: (t, t) => unit = "merge"
|
|
46
63
|
|
|
47
|
-
// Bulk-materialise
|
|
48
|
-
//
|
|
64
|
+
// Bulk-materialise transactions off the JS thread, one row per
|
|
65
|
+
// (blockNumbers[i], transactionIndices[i]) key, decoding only the fields set in
|
|
66
|
+
// that row's own masks[i]. Result is aligned with the input.
|
|
49
67
|
@send
|
|
50
68
|
external materialize: (
|
|
51
69
|
t,
|
|
52
70
|
~blockNumbers: array<int>,
|
|
53
71
|
~transactionIndices: array<int>,
|
|
54
|
-
~
|
|
72
|
+
~masks: array<float>,
|
|
55
73
|
) => promise<array<Internal.eventTransaction>> = "materialize"
|
|
56
74
|
|
|
57
75
|
// Drop transactions for blocks at or below the given block (already processed).
|
|
@@ -60,69 +78,73 @@ external materialize: (
|
|
|
60
78
|
// Drop transactions for blocks above the given block (rolled back).
|
|
61
79
|
@send external rollback: (t, int) => unit = "rollback"
|
|
62
80
|
|
|
63
|
-
// Materialise
|
|
64
|
-
// resulting transaction onto
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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 = []
|
|
81
|
+
// Materialise each store-backed item's selected transaction fields and write the
|
|
82
|
+
// resulting transaction onto its payload. Every event's mask comes from its own
|
|
83
|
+
// `eventConfig.transactionFieldMask`, so a transaction decodes only the fields
|
|
84
|
+
// the events on it selected — a large field (e.g. `input`) never materialises
|
|
85
|
+
// for events that didn't ask for it. Items that already carry an inline
|
|
86
|
+
// transaction (RPC/simulate/Fuel) are skipped. Store-backed items always get a
|
|
87
|
+
// transaction object — the selected fields, or `{}` when nothing was selected —
|
|
88
|
+
// so `event.transaction` is never `undefined` (matching the inline sources).
|
|
89
|
+
// Deduped per (blockNumber, transactionIndex); each row's mask is the OR of the
|
|
90
|
+
// masks of the events sharing that transaction.
|
|
91
|
+
let materializeItems = async (store: t, ~items: array<Internal.item>) => {
|
|
92
|
+
// Store-backed items arrive in (block, logIndex) order, and a transaction's
|
|
93
|
+
// logs are contiguous within a block, so events sharing a (blockNumber,
|
|
94
|
+
// transactionIndex) are adjacent regardless of event. Group them by extending
|
|
95
|
+
// the current run rather than hashing a string key per item. A key recurring
|
|
96
|
+
// non-adjacently just splits into two groups (one redundant decode) — never
|
|
97
|
+
// incorrect.
|
|
98
|
+
let blockNumbers = []
|
|
99
|
+
let transactionIndices = []
|
|
100
|
+
let masks = []
|
|
101
|
+
let payloadGroups = []
|
|
102
|
+
let anySelected = ref(false)
|
|
94
103
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
104
|
+
items->Array.forEach(item =>
|
|
105
|
+
switch item {
|
|
106
|
+
| Internal.Event(_) =>
|
|
107
|
+
let eventItem = item->Internal.castUnsafeEventItem
|
|
108
|
+
switch eventItem.payload->Internal.getPayloadTransaction->Nullable.toOption {
|
|
109
|
+
| Some(_) => () // RPC/simulate/Fuel carry the transaction inline.
|
|
110
|
+
| None =>
|
|
111
|
+
let {blockNumber, transactionIndex} = eventItem
|
|
112
|
+
let mask = eventItem.eventConfig.transactionFieldMask
|
|
113
|
+
if mask != 0. {
|
|
114
|
+
anySelected := true
|
|
115
|
+
}
|
|
116
|
+
let last = payloadGroups->Array.length - 1
|
|
117
|
+
if (
|
|
118
|
+
last >= 0 &&
|
|
119
|
+
blockNumbers->Array.getUnsafe(last) == blockNumber &&
|
|
120
|
+
transactionIndices->Array.getUnsafe(last) == transactionIndex
|
|
121
|
+
) {
|
|
122
|
+
payloadGroups->Array.getUnsafe(last)->Array.push(eventItem.payload)
|
|
123
|
+
masks->Array.setUnsafe(last, orMask(masks->Array.getUnsafe(last), mask))
|
|
124
|
+
} else {
|
|
125
|
+
blockNumbers->Array.push(blockNumber)
|
|
126
|
+
transactionIndices->Array.push(transactionIndex)
|
|
127
|
+
masks->Array.push(mask)
|
|
128
|
+
payloadGroups->Array.push([eventItem.payload])
|
|
115
129
|
}
|
|
116
|
-
| Internal.Block(_) => ()
|
|
117
130
|
}
|
|
118
|
-
)
|
|
131
|
+
| Internal.Block(_) => ()
|
|
132
|
+
}
|
|
133
|
+
)
|
|
119
134
|
|
|
120
|
-
|
|
121
|
-
|
|
135
|
+
if payloadGroups->Utils.Array.notEmpty {
|
|
136
|
+
if anySelected.contents {
|
|
137
|
+
let txs = await store->materialize(~blockNumbers, ~transactionIndices, ~masks)
|
|
122
138
|
payloadGroups->Array.forEachWithIndex((payloads, i) => {
|
|
123
139
|
let tx = txs->Array.getUnsafe(i)
|
|
124
140
|
payloads->Array.forEach(payload => payload->Internal.setPayloadTransaction(tx))
|
|
125
141
|
})
|
|
142
|
+
} else {
|
|
143
|
+
// No event selected any field: stamp an empty transaction object so
|
|
144
|
+
// `event.transaction` is never undefined, without a materialize call.
|
|
145
|
+
payloadGroups->Array.forEach(payloads =>
|
|
146
|
+
payloads->Array.forEach(payload => payload->Internal.setPayloadTransaction(%raw(`{}`)))
|
|
147
|
+
)
|
|
126
148
|
}
|
|
127
149
|
}
|
|
128
150
|
}
|
|
@@ -2,10 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
import * as Core from "../Core.res.mjs";
|
|
4
4
|
import * as Utils from "../Utils.res.mjs";
|
|
5
|
-
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
6
5
|
|
|
7
|
-
function make() {
|
|
8
|
-
|
|
6
|
+
function make(ecosystem, shouldChecksum) {
|
|
7
|
+
let ctor = Core.getAddon().TransactionStore;
|
|
8
|
+
switch (ecosystem) {
|
|
9
|
+
case "evm" :
|
|
10
|
+
return ctor.newEvm(shouldChecksum);
|
|
11
|
+
case "fuel" :
|
|
12
|
+
return ctor.newFuel();
|
|
13
|
+
case "svm" :
|
|
14
|
+
return ctor.newSvm();
|
|
15
|
+
}
|
|
9
16
|
}
|
|
10
17
|
|
|
11
18
|
function fieldCodes(fields) {
|
|
@@ -18,43 +25,35 @@ function fieldCodes(fields) {
|
|
|
18
25
|
|
|
19
26
|
let pow2 = (c => Math.pow(2, c));
|
|
20
27
|
|
|
21
|
-
function
|
|
22
|
-
let
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
28
|
+
function maskFromFields(selectedTransactionFields, codes) {
|
|
29
|
+
let mask = {
|
|
30
|
+
contents: 0
|
|
31
|
+
};
|
|
32
|
+
selectedTransactionFields.forEach(name => {
|
|
33
|
+
let code = codes[name];
|
|
34
|
+
if (code !== undefined) {
|
|
35
|
+
mask.contents = mask.contents + pow2(code);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
31
38
|
});
|
|
32
|
-
return
|
|
39
|
+
return mask.contents;
|
|
33
40
|
}
|
|
34
41
|
|
|
35
42
|
function makeMaskFn(fields) {
|
|
36
43
|
let codes = fieldCodes(fields);
|
|
37
|
-
return
|
|
44
|
+
return selectedTransactionFields => maskFromFields(selectedTransactionFields, codes);
|
|
38
45
|
}
|
|
39
46
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
}
|
|
47
|
+
let orMask = ((a, b) => (a | b) >>> 0);
|
|
48
|
+
|
|
49
|
+
async function materializeItems(store, items) {
|
|
55
50
|
let blockNumbers = [];
|
|
56
51
|
let transactionIndices = [];
|
|
52
|
+
let masks = [];
|
|
57
53
|
let payloadGroups = [];
|
|
54
|
+
let anySelected = {
|
|
55
|
+
contents: false
|
|
56
|
+
};
|
|
58
57
|
items.forEach(item => {
|
|
59
58
|
if (item.kind !== 0) {
|
|
60
59
|
return;
|
|
@@ -65,23 +64,37 @@ async function materializeItems(store, items, mask) {
|
|
|
65
64
|
}
|
|
66
65
|
let transactionIndex = item.transactionIndex;
|
|
67
66
|
let blockNumber = item.blockNumber;
|
|
67
|
+
let mask = item.eventConfig.transactionFieldMask;
|
|
68
|
+
if (mask !== 0) {
|
|
69
|
+
anySelected.contents = true;
|
|
70
|
+
}
|
|
68
71
|
let last = payloadGroups.length - 1 | 0;
|
|
69
72
|
if (last >= 0 && blockNumbers[last] === blockNumber && transactionIndices[last] === transactionIndex) {
|
|
70
73
|
payloadGroups[last].push(item.payload);
|
|
74
|
+
masks[last] = orMask(masks[last], mask);
|
|
71
75
|
} else {
|
|
72
76
|
blockNumbers.push(blockNumber);
|
|
73
77
|
transactionIndices.push(transactionIndex);
|
|
78
|
+
masks.push(mask);
|
|
74
79
|
payloadGroups.push([item.payload]);
|
|
75
80
|
}
|
|
76
81
|
});
|
|
77
82
|
if (!Utils.$$Array.notEmpty(payloadGroups)) {
|
|
78
83
|
return;
|
|
79
84
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
85
|
+
if (anySelected.contents) {
|
|
86
|
+
let txs = await store.materialize(blockNumbers, transactionIndices, masks);
|
|
87
|
+
payloadGroups.forEach((payloads, i) => {
|
|
88
|
+
let tx = txs[i];
|
|
89
|
+
payloads.forEach(payload => {
|
|
90
|
+
payload.transaction = tx;
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
payloadGroups.forEach(payloads => {
|
|
83
96
|
payloads.forEach(payload => {
|
|
84
|
-
payload.transaction =
|
|
97
|
+
payload.transaction = {};
|
|
85
98
|
});
|
|
86
99
|
});
|
|
87
100
|
}
|
|
@@ -90,8 +103,9 @@ export {
|
|
|
90
103
|
make,
|
|
91
104
|
fieldCodes,
|
|
92
105
|
pow2,
|
|
93
|
-
|
|
106
|
+
maskFromFields,
|
|
94
107
|
makeMaskFn,
|
|
108
|
+
orMask,
|
|
95
109
|
materializeItems,
|
|
96
110
|
}
|
|
97
111
|
/* Core Not a pure module */
|