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.
- package/index.d.ts +23 -21
- 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 +42 -6
- package/src/ChainFetching.res.mjs +30 -11
- package/src/ChainMetadata.res +1 -11
- package/src/ChainMetadata.res.mjs +1 -10
- package/src/ChainState.res +150 -1
- package/src/ChainState.res.mjs +138 -9
- package/src/ChainState.resi +51 -1
- package/src/Config.res +3 -6
- package/src/Config.res.mjs +2 -3
- package/src/Core.res +7 -0
- package/src/CrossChainState.res +24 -26
- package/src/CrossChainState.res.mjs +19 -16
- package/src/Ecosystem.res +6 -3
- package/src/Envio.res +3 -2
- package/src/EventConfigBuilder.res +61 -23
- package/src/EventConfigBuilder.res.mjs +27 -13
- package/src/EventProcessing.res +63 -21
- package/src/EventProcessing.res.mjs +47 -22
- package/src/FetchState.res +53 -43
- package/src/FetchState.res.mjs +71 -54
- package/src/HandlerLoader.res +10 -1
- package/src/HandlerLoader.res.mjs +15 -8
- package/src/Internal.res +46 -7
- package/src/Internal.res.mjs +19 -1
- package/src/LoadLayer.res +5 -5
- package/src/LoadLayer.res.mjs +6 -6
- package/src/Main.res +3 -50
- package/src/Main.res.mjs +2 -23
- package/src/PgStorage.res +4 -4
- package/src/PgStorage.res.mjs +5 -5
- package/src/Prometheus.res +10 -10
- package/src/Prometheus.res.mjs +9 -9
- package/src/PruneStaleHistory.res +2 -2
- package/src/PruneStaleHistory.res.mjs +3 -3
- package/src/Rollback.res +3 -3
- package/src/Rollback.res.mjs +4 -4
- package/src/SimulateItems.res +80 -15
- package/src/SimulateItems.res.mjs +65 -21
- package/src/TestIndexer.res +48 -36
- package/src/TestIndexer.res.mjs +35 -28
- package/src/bindings/Performance.res +7 -0
- package/src/bindings/Performance.res.mjs +21 -0
- package/src/bindings/Performance.resi +7 -0
- package/src/sources/EventRouter.res +17 -21
- package/src/sources/EventRouter.res.mjs +6 -9
- package/src/sources/Evm.res +47 -67
- package/src/sources/Evm.res.mjs +42 -65
- package/src/sources/Fuel.res +3 -3
- package/src/sources/Fuel.res.mjs +1 -6
- package/src/sources/HyperFuelSource.res +15 -11
- package/src/sources/HyperFuelSource.res.mjs +13 -11
- package/src/sources/HyperSync.res +7 -1
- package/src/sources/HyperSync.res.mjs +6 -3
- package/src/sources/HyperSync.resi +2 -0
- package/src/sources/HyperSyncClient.res +14 -79
- package/src/sources/HyperSyncClient.res.mjs +1 -22
- package/src/sources/HyperSyncSource.res +44 -27
- package/src/sources/HyperSyncSource.res.mjs +32 -27
- package/src/sources/RpcSource.res +14 -10
- package/src/sources/RpcSource.res.mjs +11 -9
- package/src/sources/SimulateSource.res +4 -2
- package/src/sources/SimulateSource.res.mjs +1 -0
- package/src/sources/Source.res +5 -1
- package/src/sources/SourceManager.res +17 -16
- package/src/sources/SourceManager.res.mjs +10 -15
- package/src/sources/Svm.res +23 -6
- package/src/sources/Svm.res.mjs +24 -6
- package/src/sources/SvmHyperSyncClient.res +3 -29
- package/src/sources/SvmHyperSyncSource.res +78 -96
- package/src/sources/SvmHyperSyncSource.res.mjs +79 -91
- package/src/sources/TransactionStore.res +128 -0
- package/src/sources/TransactionStore.res.mjs +97 -0
- package/src/tui/Tui.res +13 -16
- package/src/tui/Tui.res.mjs +12 -14
- package/svm.schema.json +30 -37
- package/src/bindings/Hrtime.res +0 -58
- package/src/bindings/Hrtime.res.mjs +0 -90
- 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,
|
|
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
|
|
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,
|
|
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
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
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
|
|
333
|
+
let match;
|
|
320
334
|
try {
|
|
321
|
-
|
|
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
|
|
340
|
-
let
|
|
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,
|
|
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.
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
523
|
+
let timer = Performance.now();
|
|
535
524
|
let h = await client.getHeight();
|
|
536
|
-
let seconds =
|
|
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
|
|
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),
|