envio 3.3.0-alpha.3 → 3.3.0-alpha.4
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/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 +3 -12
- package/src/ChainState.res.mjs +6 -8
- package/src/ChainState.resi +0 -2
- package/src/Ecosystem.res +0 -4
- package/src/EventConfigBuilder.res +3 -0
- package/src/EventConfigBuilder.res.mjs +7 -1
- package/src/FetchState.res +10 -27
- package/src/FetchState.res.mjs +13 -56
- package/src/Internal.res +5 -0
- package/src/Main.res +17 -9
- package/src/Main.res.mjs +15 -6
- package/src/SimulateItems.res +4 -0
- package/src/TestIndexer.res +43 -17
- package/src/TestIndexer.res.mjs +16 -9
- package/src/sources/Evm.res +10 -3
- package/src/sources/Evm.res.mjs +5 -5
- package/src/sources/Fuel.res +0 -2
- package/src/sources/Fuel.res.mjs +0 -1
- package/src/sources/SourceManager.res +1 -1
- package/src/sources/SourceManager.res.mjs +1 -1
- package/src/sources/Svm.res +3 -2
- package/src/sources/Svm.res.mjs +3 -4
- package/src/sources/TransactionStore.res +84 -74
- package/src/sources/TransactionStore.res.mjs +39 -33
|
@@ -500,7 +500,7 @@ async function executeQuery(sourceManager, query, knownHeight, isRealtime) {
|
|
|
500
500
|
retry: retry
|
|
501
501
|
});
|
|
502
502
|
try {
|
|
503
|
-
let response = await source.getItemsOrThrow(query.fromBlock, toBlock, query.addressesByContractName, query.
|
|
503
|
+
let response = await source.getItemsOrThrow(query.fromBlock, toBlock, query.addressesByContractName, FetchState.deriveContractNameByAddress(query.addressesByContractName), knownHeight, query.partitionId, query.selection, retry, logger$2);
|
|
504
504
|
sourceState.lastFailedAt = undefined;
|
|
505
505
|
responseRef = response;
|
|
506
506
|
} catch (raw_error) {
|
package/src/sources/Svm.res
CHANGED
|
@@ -20,7 +20,9 @@ let transactionFields = [
|
|
|
20
20
|
"tokenBalances",
|
|
21
21
|
]
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
// One instruction's selected transaction fields → store selection bitmask.
|
|
24
|
+
// Computed per event at config build and cached on the event config.
|
|
25
|
+
let eventTransactionFieldMask = TransactionStore.makeMaskFn(transactionFields)
|
|
24
26
|
|
|
25
27
|
let make = (~logger: Pino.t): Ecosystem.t => {
|
|
26
28
|
name: Svm,
|
|
@@ -36,7 +38,6 @@ let make = (~logger: Pino.t): Ecosystem.t => {
|
|
|
36
38
|
// parse. The schema is a no-op object that always surfaces `None`.
|
|
37
39
|
onEventBlockFilterSchema: S.object(_ => None),
|
|
38
40
|
logger,
|
|
39
|
-
transactionFieldMask,
|
|
40
41
|
toEvent: eventItem => eventItem.payload->(Utils.magic: Internal.eventPayload => Internal.event),
|
|
41
42
|
toEventLogger: eventItem => {
|
|
42
43
|
let instruction =
|
package/src/sources/Svm.res.mjs
CHANGED
|
@@ -30,7 +30,7 @@ let transactionFields = [
|
|
|
30
30
|
"tokenBalances"
|
|
31
31
|
];
|
|
32
32
|
|
|
33
|
-
let
|
|
33
|
+
let eventTransactionFieldMask = TransactionStore.makeMaskFn(transactionFields);
|
|
34
34
|
|
|
35
35
|
function make(logger) {
|
|
36
36
|
return {
|
|
@@ -44,7 +44,6 @@ function make(logger) {
|
|
|
44
44
|
onEventBlockFilterSchema: S$RescriptSchema.object(param => {}),
|
|
45
45
|
logger: logger,
|
|
46
46
|
toEvent: eventItem => eventItem.payload,
|
|
47
|
-
transactionFieldMask: transactionFieldMask,
|
|
48
47
|
toEventLogger: eventItem => {
|
|
49
48
|
let instruction = eventItem.payload;
|
|
50
49
|
return Logging.createChildFrom(logger, {
|
|
@@ -97,9 +96,9 @@ function makeRPCSource(chain, rpc, sourceForOpt) {
|
|
|
97
96
|
export {
|
|
98
97
|
cleanUpRawEventFieldsInPlace,
|
|
99
98
|
transactionFields,
|
|
100
|
-
|
|
99
|
+
eventTransactionFieldMask,
|
|
101
100
|
make,
|
|
102
101
|
GetFinalizedSlot,
|
|
103
102
|
makeRPCSource,
|
|
104
103
|
}
|
|
105
|
-
/*
|
|
104
|
+
/* eventTransactionFieldMask Not a pure module */
|
|
@@ -19,39 +19,45 @@ let fieldCodes = (fields: array<string>): dict<int> => {
|
|
|
19
19
|
|
|
20
20
|
let pow2: int => float = %raw(`c => Math.pow(2, c)`)
|
|
21
21
|
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
22
|
+
// One event's selected transaction fields as a bitmask float (bit `code` set ⇔
|
|
23
|
+
// selected). Each field appears once, so summing `pow2(code)` sets distinct bits
|
|
24
|
+
// with no overlap; the result stays exact in f64 (codes span 0..31, so a mask is
|
|
25
|
+
// at most 2^32-1).
|
|
26
|
+
let maskFromFields = (selectedTransactionFields: Utils.Set.t<string>, ~codes: dict<int>): float => {
|
|
27
|
+
let mask = ref(0.)
|
|
28
|
+
selectedTransactionFields->Utils.Set.forEach(name =>
|
|
29
|
+
switch codes->Utils.Dict.dangerouslyGetNonOption(name) {
|
|
30
|
+
| Some(code) => mask := mask.contents +. pow2(code)
|
|
31
|
+
| None => ()
|
|
32
|
+
}
|
|
33
33
|
)
|
|
34
|
-
|
|
34
|
+
mask.contents
|
|
35
35
|
}
|
|
36
36
|
|
|
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>): (
|
|
37
|
+
// Build an ecosystem's per-event mask function from its ordered field-name
|
|
38
|
+
// array. The field codes are derived once and closed over.
|
|
39
|
+
let makeMaskFn = (fields: array<string>): (Utils.Set.t<string> => float) => {
|
|
40
40
|
let codes = fieldCodes(fields)
|
|
41
|
-
|
|
41
|
+
selectedTransactionFields => selectedTransactionFields->maskFromFields(~codes)
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
// Bitwise OR of two per-event masks. Masks fit in 32 bits (≤32 transaction
|
|
45
|
+
// fields), so `>>> 0` recovers the unsigned value that a plain `|` renders
|
|
46
|
+
// negative once bit 31 is set.
|
|
47
|
+
let orMask: (float, float) => float = %raw(`(a, b) => (a | b) >>> 0`)
|
|
48
|
+
|
|
44
49
|
// Drain another store (a fetch-response page) into this one.
|
|
45
50
|
@send external merge: (t, t) => unit = "merge"
|
|
46
51
|
|
|
47
|
-
// Bulk-materialise
|
|
48
|
-
//
|
|
52
|
+
// Bulk-materialise transactions off the JS thread, one row per
|
|
53
|
+
// (blockNumbers[i], transactionIndices[i]) key, decoding only the fields set in
|
|
54
|
+
// that row's own masks[i]. Result is aligned with the input.
|
|
49
55
|
@send
|
|
50
56
|
external materialize: (
|
|
51
57
|
t,
|
|
52
58
|
~blockNumbers: array<int>,
|
|
53
59
|
~transactionIndices: array<int>,
|
|
54
|
-
~
|
|
60
|
+
~masks: array<float>,
|
|
55
61
|
) => promise<array<Internal.eventTransaction>> = "materialize"
|
|
56
62
|
|
|
57
63
|
// Drop transactions for blocks at or below the given block (already processed).
|
|
@@ -60,69 +66,73 @@ external materialize: (
|
|
|
60
66
|
// Drop transactions for blocks above the given block (rolled back).
|
|
61
67
|
@send external rollback: (t, int) => unit = "rollback"
|
|
62
68
|
|
|
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 = []
|
|
69
|
+
// Materialise each store-backed item's selected transaction fields and write the
|
|
70
|
+
// resulting transaction onto its payload. Every event's mask comes from its own
|
|
71
|
+
// `eventConfig.transactionFieldMask`, so a transaction decodes only the fields
|
|
72
|
+
// the events on it selected — a large field (e.g. `input`) never materialises
|
|
73
|
+
// for events that didn't ask for it. Items that already carry an inline
|
|
74
|
+
// transaction (RPC/simulate/Fuel) are skipped. Store-backed items always get a
|
|
75
|
+
// transaction object — the selected fields, or `{}` when nothing was selected —
|
|
76
|
+
// so `event.transaction` is never `undefined` (matching the inline sources).
|
|
77
|
+
// Deduped per (blockNumber, transactionIndex); each row's mask is the OR of the
|
|
78
|
+
// masks of the events sharing that transaction.
|
|
79
|
+
let materializeItems = async (store: t, ~items: array<Internal.item>) => {
|
|
80
|
+
// Store-backed items arrive in (block, logIndex) order, and a transaction's
|
|
81
|
+
// logs are contiguous within a block, so events sharing a (blockNumber,
|
|
82
|
+
// transactionIndex) are adjacent regardless of event. Group them by extending
|
|
83
|
+
// the current run rather than hashing a string key per item. A key recurring
|
|
84
|
+
// non-adjacently just splits into two groups (one redundant decode) — never
|
|
85
|
+
// incorrect.
|
|
86
|
+
let blockNumbers = []
|
|
87
|
+
let transactionIndices = []
|
|
88
|
+
let masks = []
|
|
89
|
+
let payloadGroups = []
|
|
90
|
+
let anySelected = ref(false)
|
|
94
91
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
92
|
+
items->Array.forEach(item =>
|
|
93
|
+
switch item {
|
|
94
|
+
| Internal.Event(_) =>
|
|
95
|
+
let eventItem = item->Internal.castUnsafeEventItem
|
|
96
|
+
switch eventItem.payload->Internal.getPayloadTransaction->Nullable.toOption {
|
|
97
|
+
| Some(_) => () // RPC/simulate/Fuel carry the transaction inline.
|
|
98
|
+
| None =>
|
|
99
|
+
let {blockNumber, transactionIndex} = eventItem
|
|
100
|
+
let mask = eventItem.eventConfig.transactionFieldMask
|
|
101
|
+
if mask != 0. {
|
|
102
|
+
anySelected := true
|
|
103
|
+
}
|
|
104
|
+
let last = payloadGroups->Array.length - 1
|
|
105
|
+
if (
|
|
106
|
+
last >= 0 &&
|
|
107
|
+
blockNumbers->Array.getUnsafe(last) == blockNumber &&
|
|
108
|
+
transactionIndices->Array.getUnsafe(last) == transactionIndex
|
|
109
|
+
) {
|
|
110
|
+
payloadGroups->Array.getUnsafe(last)->Array.push(eventItem.payload)
|
|
111
|
+
masks->Array.setUnsafe(last, orMask(masks->Array.getUnsafe(last), mask))
|
|
112
|
+
} else {
|
|
113
|
+
blockNumbers->Array.push(blockNumber)
|
|
114
|
+
transactionIndices->Array.push(transactionIndex)
|
|
115
|
+
masks->Array.push(mask)
|
|
116
|
+
payloadGroups->Array.push([eventItem.payload])
|
|
115
117
|
}
|
|
116
|
-
| Internal.Block(_) => ()
|
|
117
118
|
}
|
|
118
|
-
)
|
|
119
|
+
| Internal.Block(_) => ()
|
|
120
|
+
}
|
|
121
|
+
)
|
|
119
122
|
|
|
120
|
-
|
|
121
|
-
|
|
123
|
+
if payloadGroups->Utils.Array.notEmpty {
|
|
124
|
+
if anySelected.contents {
|
|
125
|
+
let txs = await store->materialize(~blockNumbers, ~transactionIndices, ~masks)
|
|
122
126
|
payloadGroups->Array.forEachWithIndex((payloads, i) => {
|
|
123
127
|
let tx = txs->Array.getUnsafe(i)
|
|
124
128
|
payloads->Array.forEach(payload => payload->Internal.setPayloadTransaction(tx))
|
|
125
129
|
})
|
|
130
|
+
} else {
|
|
131
|
+
// No event selected any field: stamp an empty transaction object so
|
|
132
|
+
// `event.transaction` is never undefined, without a materialize call.
|
|
133
|
+
payloadGroups->Array.forEach(payloads =>
|
|
134
|
+
payloads->Array.forEach(payload => payload->Internal.setPayloadTransaction(%raw(`{}`)))
|
|
135
|
+
)
|
|
126
136
|
}
|
|
127
137
|
}
|
|
128
138
|
}
|
|
@@ -2,7 +2,6 @@
|
|
|
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
6
|
function make() {
|
|
8
7
|
return Core.getAddon().TransactionStore.new();
|
|
@@ -18,43 +17,35 @@ function fieldCodes(fields) {
|
|
|
18
17
|
|
|
19
18
|
let pow2 = (c => Math.pow(2, c));
|
|
20
19
|
|
|
21
|
-
function
|
|
22
|
-
let
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
20
|
+
function maskFromFields(selectedTransactionFields, codes) {
|
|
21
|
+
let mask = {
|
|
22
|
+
contents: 0
|
|
23
|
+
};
|
|
24
|
+
selectedTransactionFields.forEach(name => {
|
|
25
|
+
let code = codes[name];
|
|
26
|
+
if (code !== undefined) {
|
|
27
|
+
mask.contents = mask.contents + pow2(code);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
31
30
|
});
|
|
32
|
-
return
|
|
31
|
+
return mask.contents;
|
|
33
32
|
}
|
|
34
33
|
|
|
35
34
|
function makeMaskFn(fields) {
|
|
36
35
|
let codes = fieldCodes(fields);
|
|
37
|
-
return
|
|
36
|
+
return selectedTransactionFields => maskFromFields(selectedTransactionFields, codes);
|
|
38
37
|
}
|
|
39
38
|
|
|
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
|
-
}
|
|
39
|
+
let orMask = ((a, b) => (a | b) >>> 0);
|
|
40
|
+
|
|
41
|
+
async function materializeItems(store, items) {
|
|
55
42
|
let blockNumbers = [];
|
|
56
43
|
let transactionIndices = [];
|
|
44
|
+
let masks = [];
|
|
57
45
|
let payloadGroups = [];
|
|
46
|
+
let anySelected = {
|
|
47
|
+
contents: false
|
|
48
|
+
};
|
|
58
49
|
items.forEach(item => {
|
|
59
50
|
if (item.kind !== 0) {
|
|
60
51
|
return;
|
|
@@ -65,23 +56,37 @@ async function materializeItems(store, items, mask) {
|
|
|
65
56
|
}
|
|
66
57
|
let transactionIndex = item.transactionIndex;
|
|
67
58
|
let blockNumber = item.blockNumber;
|
|
59
|
+
let mask = item.eventConfig.transactionFieldMask;
|
|
60
|
+
if (mask !== 0) {
|
|
61
|
+
anySelected.contents = true;
|
|
62
|
+
}
|
|
68
63
|
let last = payloadGroups.length - 1 | 0;
|
|
69
64
|
if (last >= 0 && blockNumbers[last] === blockNumber && transactionIndices[last] === transactionIndex) {
|
|
70
65
|
payloadGroups[last].push(item.payload);
|
|
66
|
+
masks[last] = orMask(masks[last], mask);
|
|
71
67
|
} else {
|
|
72
68
|
blockNumbers.push(blockNumber);
|
|
73
69
|
transactionIndices.push(transactionIndex);
|
|
70
|
+
masks.push(mask);
|
|
74
71
|
payloadGroups.push([item.payload]);
|
|
75
72
|
}
|
|
76
73
|
});
|
|
77
74
|
if (!Utils.$$Array.notEmpty(payloadGroups)) {
|
|
78
75
|
return;
|
|
79
76
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
77
|
+
if (anySelected.contents) {
|
|
78
|
+
let txs = await store.materialize(blockNumbers, transactionIndices, masks);
|
|
79
|
+
payloadGroups.forEach((payloads, i) => {
|
|
80
|
+
let tx = txs[i];
|
|
81
|
+
payloads.forEach(payload => {
|
|
82
|
+
payload.transaction = tx;
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
payloadGroups.forEach(payloads => {
|
|
83
88
|
payloads.forEach(payload => {
|
|
84
|
-
payload.transaction =
|
|
89
|
+
payload.transaction = {};
|
|
85
90
|
});
|
|
86
91
|
});
|
|
87
92
|
}
|
|
@@ -90,8 +95,9 @@ export {
|
|
|
90
95
|
make,
|
|
91
96
|
fieldCodes,
|
|
92
97
|
pow2,
|
|
93
|
-
|
|
98
|
+
maskFromFields,
|
|
94
99
|
makeMaskFn,
|
|
100
|
+
orMask,
|
|
95
101
|
materializeItems,
|
|
96
102
|
}
|
|
97
103
|
/* Core Not a pure module */
|