envio 3.3.0-alpha.4 → 3.3.0-alpha.6
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/BatchProcessing.res +5 -0
- package/src/BatchProcessing.res.mjs +6 -0
- package/src/ChainState.res +41 -7
- package/src/ChainState.res.mjs +22 -18
- package/src/ChainState.resi +3 -1
- package/src/Config.res +3 -0
- package/src/Config.res.mjs +3 -1
- package/src/Core.res +0 -3
- package/src/ExitOnCaughtUp.res +10 -2
- package/src/ExitOnCaughtUp.res.mjs +10 -1
- package/src/FetchState.res +63 -133
- package/src/FetchState.res.mjs +107 -166
- package/src/IndexerState.res +4 -0
- package/src/IndexerState.res.mjs +8 -1
- package/src/IndexerState.resi +1 -0
- package/src/IndexingAddresses.res +105 -0
- package/src/IndexingAddresses.res.mjs +100 -0
- package/src/IndexingAddresses.resi +32 -0
- package/src/Main.res +4 -15
- package/src/Main.res.mjs +6 -14
- 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/SimulateDeadInputTracker.res +85 -0
- package/src/SimulateDeadInputTracker.res.mjs +73 -0
- package/src/SimulateDeadInputTracker.resi +12 -0
- package/src/SimulateItems.res +23 -42
- package/src/SimulateItems.res.mjs +12 -30
- package/src/TestIndexer.res +3 -27
- package/src/TestIndexer.res.mjs +2 -9
- package/src/bindings/Viem.res +0 -41
- package/src/bindings/Viem.res.mjs +1 -43
- package/src/sources/Evm.res +7 -36
- package/src/sources/Evm.res.mjs +4 -36
- package/src/sources/EvmChain.res +4 -2
- package/src/sources/EvmChain.res.mjs +3 -2
- package/src/sources/EvmRpcClient.res +36 -5
- package/src/sources/EvmRpcClient.res.mjs +7 -4
- 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 +289 -204
- package/src/sources/RpcSource.res.mjs +293 -303
- package/src/sources/SimulateSource.res +1 -0
- package/src/sources/SimulateSource.res.mjs +2 -1
- package/src/sources/Source.res +4 -0
- package/src/sources/Svm.res +7 -15
- package/src/sources/Svm.res.mjs +4 -15
- package/src/sources/TransactionStore.res +14 -2
- package/src/sources/TransactionStore.res.mjs +10 -2
package/src/Prometheus.res.mjs
CHANGED
|
@@ -389,39 +389,28 @@ let ProcessStartTimeSeconds = {
|
|
|
389
389
|
set: set$3
|
|
390
390
|
};
|
|
391
391
|
|
|
392
|
-
let gauge$4 = makeOrThrow$1("
|
|
392
|
+
let gauge$4 = makeOrThrow$1("envio_indexing_concurrency", "The number of executing concurrent queries to the chain data-source.", chainIdLabelsSchema);
|
|
393
393
|
|
|
394
|
-
function set$4(
|
|
395
|
-
handleInt$1(gauge$4, chainId,
|
|
394
|
+
function set$4(concurrency, chainId) {
|
|
395
|
+
handleInt$1(gauge$4, chainId, concurrency);
|
|
396
396
|
}
|
|
397
397
|
|
|
398
|
-
let
|
|
398
|
+
let IndexingConcurrency = {
|
|
399
399
|
gauge: gauge$4,
|
|
400
400
|
set: set$4
|
|
401
401
|
};
|
|
402
402
|
|
|
403
|
-
let gauge$5 = makeOrThrow$1("
|
|
403
|
+
let gauge$5 = makeOrThrow$1("envio_indexing_partitions", "The number of partitions used to split fetching logic by addresses and block ranges.", chainIdLabelsSchema);
|
|
404
404
|
|
|
405
|
-
function set$5(
|
|
406
|
-
handleInt$1(gauge$5, chainId,
|
|
405
|
+
function set$5(partitionsCount, chainId) {
|
|
406
|
+
handleInt$1(gauge$5, chainId, partitionsCount);
|
|
407
407
|
}
|
|
408
408
|
|
|
409
|
-
let
|
|
409
|
+
let IndexingPartitions = {
|
|
410
410
|
gauge: gauge$5,
|
|
411
411
|
set: set$5
|
|
412
412
|
};
|
|
413
413
|
|
|
414
|
-
let gauge$6 = makeOrThrow$1("envio_indexing_partitions", "The number of partitions used to split fetching logic by addresses and block ranges.", chainIdLabelsSchema);
|
|
415
|
-
|
|
416
|
-
function set$6(partitionsCount, chainId) {
|
|
417
|
-
handleInt$1(gauge$6, chainId, partitionsCount);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
let IndexingPartitions = {
|
|
421
|
-
gauge: gauge$6,
|
|
422
|
-
set: set$6
|
|
423
|
-
};
|
|
424
|
-
|
|
425
414
|
let counter = makeOrThrow("envio_indexing_idle_seconds", "The time the indexer source syncing has been idle. A high value may indicate the source sync is a bottleneck.", chainIdLabelsSchema);
|
|
426
415
|
|
|
427
416
|
let IndexingIdleTime = {
|
|
@@ -440,51 +429,51 @@ let IndexingQueryTime = {
|
|
|
440
429
|
counter: counter$2
|
|
441
430
|
};
|
|
442
431
|
|
|
443
|
-
let gauge$
|
|
432
|
+
let gauge$6 = makeOrThrow$1("envio_indexing_buffer_size", "The current number of items in the indexing buffer.", chainIdLabelsSchema);
|
|
444
433
|
|
|
445
|
-
function set$
|
|
446
|
-
handleInt$1(gauge$
|
|
434
|
+
function set$6(bufferSize, chainId) {
|
|
435
|
+
handleInt$1(gauge$6, chainId, bufferSize);
|
|
447
436
|
}
|
|
448
437
|
|
|
449
438
|
let IndexingBufferSize = {
|
|
450
|
-
gauge: gauge$
|
|
451
|
-
set: set$
|
|
439
|
+
gauge: gauge$6,
|
|
440
|
+
set: set$6
|
|
452
441
|
};
|
|
453
442
|
|
|
454
|
-
let gauge$
|
|
443
|
+
let gauge$7 = PromClient.Gauge.makeGauge({
|
|
455
444
|
name: "envio_indexing_target_buffer_size",
|
|
456
445
|
help: "The indexer-wide target buffer size shared across all chains. The actual number of items in the queue may exceed this value, but the indexer always tries to keep the buffer filled up to this target."
|
|
457
446
|
});
|
|
458
447
|
|
|
459
|
-
function set$
|
|
460
|
-
gauge$
|
|
448
|
+
function set$7(targetBufferSize) {
|
|
449
|
+
gauge$7.set(targetBufferSize);
|
|
461
450
|
}
|
|
462
451
|
|
|
463
452
|
let IndexingTargetBufferSize = {
|
|
464
|
-
gauge: gauge$
|
|
465
|
-
set: set$
|
|
453
|
+
gauge: gauge$7,
|
|
454
|
+
set: set$7
|
|
466
455
|
};
|
|
467
456
|
|
|
468
|
-
let gauge$
|
|
457
|
+
let gauge$8 = makeOrThrow$1("envio_indexing_buffer_block", "The highest block number that has been fully fetched by the indexer.", chainIdLabelsSchema);
|
|
469
458
|
|
|
470
|
-
function set$
|
|
471
|
-
handleInt$1(gauge$
|
|
459
|
+
function set$8(blockNumber, chainId) {
|
|
460
|
+
handleInt$1(gauge$8, chainId, blockNumber);
|
|
472
461
|
}
|
|
473
462
|
|
|
474
463
|
let IndexingBufferBlockNumber = {
|
|
475
|
-
gauge: gauge$
|
|
476
|
-
set: set$
|
|
464
|
+
gauge: gauge$8,
|
|
465
|
+
set: set$8
|
|
477
466
|
};
|
|
478
467
|
|
|
479
|
-
let gauge$
|
|
468
|
+
let gauge$9 = makeOrThrow$1("envio_indexing_end_block", "The block number to stop indexing at. (inclusive)", chainIdLabelsSchema);
|
|
480
469
|
|
|
481
|
-
function set$
|
|
482
|
-
handleInt$1(gauge$
|
|
470
|
+
function set$9(endBlock, chainId) {
|
|
471
|
+
handleInt$1(gauge$9, chainId, endBlock);
|
|
483
472
|
}
|
|
484
473
|
|
|
485
474
|
let IndexingEndBlock = {
|
|
486
|
-
gauge: gauge$
|
|
487
|
-
set: set$
|
|
475
|
+
gauge: gauge$9,
|
|
476
|
+
set: set$9
|
|
488
477
|
};
|
|
489
478
|
|
|
490
479
|
let sourceLabelsSchema = S$RescriptSchema.schema(s => ({
|
|
@@ -525,18 +514,18 @@ let SourceRequestCount = {
|
|
|
525
514
|
addSeconds: addSeconds
|
|
526
515
|
};
|
|
527
516
|
|
|
528
|
-
let gauge$
|
|
517
|
+
let gauge$10 = makeOrThrow$1("envio_source_known_height", "The latest known block number reported by the source. This value may lag behind the actual chain height, as it is updated only when queried.", sourceLabelsSchema);
|
|
529
518
|
|
|
530
|
-
function set$
|
|
531
|
-
handleInt$1(gauge$
|
|
519
|
+
function set$10(sourceName, chainId, blockNumber) {
|
|
520
|
+
handleInt$1(gauge$10, {
|
|
532
521
|
source: sourceName,
|
|
533
522
|
chainId: chainId
|
|
534
523
|
}, blockNumber);
|
|
535
524
|
}
|
|
536
525
|
|
|
537
526
|
let SourceHeight = {
|
|
538
|
-
gauge: gauge$
|
|
539
|
-
set: set$
|
|
527
|
+
gauge: gauge$10,
|
|
528
|
+
set: set$10
|
|
540
529
|
};
|
|
541
530
|
|
|
542
531
|
let counter$4 = makeOrThrow("envio_reorg_detected_total", "Total number of reorgs detected", chainIdLabelsSchema);
|
|
@@ -550,43 +539,43 @@ let ReorgCount = {
|
|
|
550
539
|
increment: increment$5
|
|
551
540
|
};
|
|
552
541
|
|
|
553
|
-
let gauge$
|
|
542
|
+
let gauge$11 = makeOrThrow$1("envio_reorg_detected_block", "The block number where reorg was detected the last time. This doesn't mean that the block was reorged, this is simply where we found block hash to be different.", chainIdLabelsSchema);
|
|
554
543
|
|
|
555
|
-
function set$
|
|
556
|
-
handleInt$1(gauge$
|
|
544
|
+
function set$11(blockNumber, chain) {
|
|
545
|
+
handleInt$1(gauge$11, chain, blockNumber);
|
|
557
546
|
}
|
|
558
547
|
|
|
559
548
|
let ReorgDetectionBlockNumber = {
|
|
560
|
-
gauge: gauge$
|
|
561
|
-
set: set$
|
|
549
|
+
gauge: gauge$11,
|
|
550
|
+
set: set$11
|
|
562
551
|
};
|
|
563
552
|
|
|
564
|
-
let gauge$
|
|
553
|
+
let gauge$12 = PromClient.Gauge.makeGauge({
|
|
565
554
|
name: "envio_reorg_threshold",
|
|
566
555
|
help: "Whether indexing is currently within the reorg threshold"
|
|
567
556
|
});
|
|
568
557
|
|
|
569
|
-
function set$
|
|
570
|
-
gauge$
|
|
558
|
+
function set$12(isInReorgThreshold) {
|
|
559
|
+
gauge$12.set(isInReorgThreshold ? 1 : 0);
|
|
571
560
|
}
|
|
572
561
|
|
|
573
562
|
let ReorgThreshold = {
|
|
574
|
-
gauge: gauge$
|
|
575
|
-
set: set$
|
|
563
|
+
gauge: gauge$12,
|
|
564
|
+
set: set$12
|
|
576
565
|
};
|
|
577
566
|
|
|
578
|
-
let gauge$
|
|
567
|
+
let gauge$13 = PromClient.Gauge.makeGauge({
|
|
579
568
|
name: "envio_rollback_enabled",
|
|
580
569
|
help: "Whether rollback on reorg is enabled"
|
|
581
570
|
});
|
|
582
571
|
|
|
583
|
-
function set$
|
|
584
|
-
gauge$
|
|
572
|
+
function set$13(enabled) {
|
|
573
|
+
gauge$13.set(enabled ? 1 : 0);
|
|
585
574
|
}
|
|
586
575
|
|
|
587
576
|
let RollbackEnabled = {
|
|
588
|
-
gauge: gauge$
|
|
589
|
-
set: set$
|
|
577
|
+
gauge: gauge$13,
|
|
578
|
+
set: set$13
|
|
590
579
|
};
|
|
591
580
|
|
|
592
581
|
let timeCounter$3 = PromClient.Counter.makeCounter({
|
|
@@ -635,62 +624,62 @@ let RollbackHistoryPrune = {
|
|
|
635
624
|
increment: increment$7
|
|
636
625
|
};
|
|
637
626
|
|
|
638
|
-
let gauge$
|
|
627
|
+
let gauge$14 = makeOrThrow$1("envio_rollback_target_block", "The block number reorg was rollbacked to the last time.", chainIdLabelsSchema);
|
|
639
628
|
|
|
640
|
-
function set$
|
|
641
|
-
handleInt$1(gauge$
|
|
629
|
+
function set$14(blockNumber, chain) {
|
|
630
|
+
handleInt$1(gauge$14, chain, blockNumber);
|
|
642
631
|
}
|
|
643
632
|
|
|
644
633
|
let RollbackTargetBlockNumber = {
|
|
645
|
-
gauge: gauge$
|
|
646
|
-
set: set$
|
|
634
|
+
gauge: gauge$14,
|
|
635
|
+
set: set$14
|
|
647
636
|
};
|
|
648
637
|
|
|
649
|
-
let gauge$
|
|
638
|
+
let gauge$15 = PromClient.Gauge.makeGauge({
|
|
650
639
|
name: "envio_processing_max_batch_size",
|
|
651
640
|
help: "The maximum number of items to process in a single batch."
|
|
652
641
|
});
|
|
653
642
|
|
|
654
|
-
function set$
|
|
655
|
-
gauge$
|
|
643
|
+
function set$15(maxBatchSize) {
|
|
644
|
+
gauge$15.set(maxBatchSize);
|
|
656
645
|
}
|
|
657
646
|
|
|
658
647
|
let ProcessingMaxBatchSize = {
|
|
659
|
-
gauge: gauge$
|
|
660
|
-
set: set$
|
|
648
|
+
gauge: gauge$15,
|
|
649
|
+
set: set$15
|
|
661
650
|
};
|
|
662
651
|
|
|
663
|
-
let gauge$
|
|
652
|
+
let gauge$16 = makeOrThrow$1("envio_progress_block", "The block number of the latest block processed and stored in the database.", chainIdLabelsSchema);
|
|
664
653
|
|
|
665
|
-
function set$
|
|
666
|
-
handleInt$1(gauge$
|
|
654
|
+
function set$16(blockNumber, chainId) {
|
|
655
|
+
handleInt$1(gauge$16, chainId, blockNumber);
|
|
667
656
|
}
|
|
668
657
|
|
|
669
658
|
let ProgressBlockNumber = {
|
|
670
|
-
gauge: gauge$
|
|
671
|
-
set: set$
|
|
659
|
+
gauge: gauge$16,
|
|
660
|
+
set: set$16
|
|
672
661
|
};
|
|
673
662
|
|
|
674
|
-
let gauge$
|
|
663
|
+
let gauge$17 = makeOrThrow$1("envio_progress_events", "The number of events processed and reflected in the database.", chainIdLabelsSchema);
|
|
675
664
|
|
|
676
|
-
function set$
|
|
677
|
-
handleFloat$1(gauge$
|
|
665
|
+
function set$17(processedCount, chainId) {
|
|
666
|
+
handleFloat$1(gauge$17, chainId, processedCount);
|
|
678
667
|
}
|
|
679
668
|
|
|
680
669
|
let ProgressEventsCount = {
|
|
681
|
-
gauge: gauge$
|
|
682
|
-
set: set$
|
|
670
|
+
gauge: gauge$17,
|
|
671
|
+
set: set$17
|
|
683
672
|
};
|
|
684
673
|
|
|
685
|
-
let gauge$
|
|
674
|
+
let gauge$18 = makeOrThrow$1("envio_progress_latency", "The latency in milliseconds between the latest processed event creation and the time it was written to storage.", chainIdLabelsSchema);
|
|
686
675
|
|
|
687
|
-
function set$
|
|
688
|
-
handleInt$1(gauge$
|
|
676
|
+
function set$18(latencyMs, chainId) {
|
|
677
|
+
handleInt$1(gauge$18, chainId, latencyMs);
|
|
689
678
|
}
|
|
690
679
|
|
|
691
680
|
let ProgressLatency = {
|
|
692
|
-
gauge: gauge$
|
|
693
|
-
set: set$
|
|
681
|
+
gauge: gauge$18,
|
|
682
|
+
set: set$18
|
|
694
683
|
};
|
|
695
684
|
|
|
696
685
|
let effectLabelsSchema = S$RescriptSchema.object(s => s.f("effect", S$RescriptSchema.string));
|
|
@@ -710,15 +699,15 @@ let EffectCalls = {
|
|
|
710
699
|
activeCallsCount: activeCallsCount
|
|
711
700
|
};
|
|
712
701
|
|
|
713
|
-
let gauge$
|
|
702
|
+
let gauge$19 = makeOrThrow$1("envio_effect_cache", "The number of items in the effect cache.", effectLabelsSchema);
|
|
714
703
|
|
|
715
|
-
function set$
|
|
716
|
-
handleInt$1(gauge$
|
|
704
|
+
function set$19(count, effectName) {
|
|
705
|
+
handleInt$1(gauge$19, effectName, count);
|
|
717
706
|
}
|
|
718
707
|
|
|
719
708
|
let EffectCacheCount = {
|
|
720
|
-
gauge: gauge$
|
|
721
|
-
set: set$
|
|
709
|
+
gauge: gauge$19,
|
|
710
|
+
set: set$19
|
|
722
711
|
};
|
|
723
712
|
|
|
724
713
|
let counter$7 = makeOrThrow("envio_effect_cache_invalidations", "The number of effect cache invalidations.", effectLabelsSchema);
|
|
@@ -732,18 +721,18 @@ let EffectCacheInvalidationsCount = {
|
|
|
732
721
|
increment: increment$8
|
|
733
722
|
};
|
|
734
723
|
|
|
735
|
-
let gauge$
|
|
724
|
+
let gauge$20 = makeOrThrow$1("envio_effect_queue", "The number of effect calls waiting in the rate limit queue.", effectLabelsSchema);
|
|
736
725
|
|
|
737
726
|
let timeCounter$6 = makeOrThrow("envio_effect_queue_wait_seconds", "The time spent waiting in the rate limit queue.", effectLabelsSchema);
|
|
738
727
|
|
|
739
|
-
function set$
|
|
740
|
-
handleInt$1(gauge$
|
|
728
|
+
function set$20(count, effectName) {
|
|
729
|
+
handleInt$1(gauge$20, effectName, count);
|
|
741
730
|
}
|
|
742
731
|
|
|
743
732
|
let EffectQueueCount = {
|
|
744
|
-
gauge: gauge$
|
|
733
|
+
gauge: gauge$20,
|
|
745
734
|
timeCounter: timeCounter$6,
|
|
746
|
-
set: set$
|
|
735
|
+
set: set$20
|
|
747
736
|
};
|
|
748
737
|
|
|
749
738
|
let loadLabelsSchema = S$RescriptSchema.schema(s => ({
|
|
@@ -846,7 +835,6 @@ export {
|
|
|
846
835
|
IndexingKnownHeight,
|
|
847
836
|
Info,
|
|
848
837
|
ProcessStartTimeSeconds,
|
|
849
|
-
IndexingAddresses,
|
|
850
838
|
IndexingConcurrency,
|
|
851
839
|
IndexingPartitions,
|
|
852
840
|
IndexingIdleTime,
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Lives on IndexerState and is fed each processed batch, so ChainState and the
|
|
2
|
+
// fetch loop carry no simulate-specific state.
|
|
3
|
+
|
|
4
|
+
// Match a provided item to a processed one by its (chain, block, logIndex)
|
|
5
|
+
// coordinate rather than object identity, so matching survives any copy or
|
|
6
|
+
// transform of the item between the source and the batch.
|
|
7
|
+
let itemKey = (item: Internal.item): string =>
|
|
8
|
+
switch item {
|
|
9
|
+
| Internal.Event({chain, blockNumber, logIndex}) =>
|
|
10
|
+
`${chain
|
|
11
|
+
->ChainMap.Chain.toChainId
|
|
12
|
+
->Int.toString}:${blockNumber->Int.toString}:${logIndex->Int.toString}`
|
|
13
|
+
| _ => "" // non-event items are never a provided simulate input
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// `index` is the item's position in its chain's `simulate` array, reported back
|
|
17
|
+
// so a user finds it without echoing its fields.
|
|
18
|
+
type entry = {chainId: int, index: int, key: string}
|
|
19
|
+
|
|
20
|
+
type t = {mutable unprocessed: array<entry>}
|
|
21
|
+
|
|
22
|
+
let makeFromConfig = (config: Config.t): option<t> => {
|
|
23
|
+
let entries =
|
|
24
|
+
config.chainMap
|
|
25
|
+
->ChainMap.values
|
|
26
|
+
->Array.flatMap(chainConfig =>
|
|
27
|
+
switch chainConfig.sourceConfig {
|
|
28
|
+
| Config.CustomSources(sources) =>
|
|
29
|
+
sources->Array.flatMap(source =>
|
|
30
|
+
source.simulateItems
|
|
31
|
+
->Option.getOr([])
|
|
32
|
+
->Array.mapWithIndex(
|
|
33
|
+
(item, index) => {
|
|
34
|
+
chainId: chainConfig.id,
|
|
35
|
+
index,
|
|
36
|
+
key: itemKey(item),
|
|
37
|
+
},
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
| _ => []
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
switch entries {
|
|
44
|
+
| [] => None
|
|
45
|
+
| _ => Some({unprocessed: entries})
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let recordProcessed = (t: t, ~batch: Batch.t) => {
|
|
50
|
+
let processedKeys = batch.items->Array.map(itemKey)->Utils.Set.fromArray
|
|
51
|
+
t.unprocessed = t.unprocessed->Array.filter(entry => !(processedKeys->Utils.Set.has(entry.key)))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Unrouted item indices grouped by chain, in the order chains were first seen.
|
|
55
|
+
let unroutedByChain = (t: t): array<(int, array<int>)> => {
|
|
56
|
+
let indicesByChain = Dict.make()
|
|
57
|
+
let chainOrder = []
|
|
58
|
+
t.unprocessed->Array.forEach(entry => {
|
|
59
|
+
let key = entry.chainId->Int.toString
|
|
60
|
+
if indicesByChain->Dict.get(key)->Option.isNone {
|
|
61
|
+
chainOrder->Array.push(entry.chainId)->ignore
|
|
62
|
+
}
|
|
63
|
+
indicesByChain->Utils.Dict.push(key, entry.index)
|
|
64
|
+
})
|
|
65
|
+
chainOrder->Array.map(chainId => (chainId, indicesByChain->Dict.getUnsafe(chainId->Int.toString)))
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let failureMessage = (t: t): option<string> =>
|
|
69
|
+
switch t->unroutedByChain {
|
|
70
|
+
| [] => None
|
|
71
|
+
| byChain =>
|
|
72
|
+
let count = byChain->Array.reduce(0, (acc, (_chainId, indices)) => acc + indices->Array.length)
|
|
73
|
+
let itemWord = count === 1 ? "item" : "items"
|
|
74
|
+
let lines =
|
|
75
|
+
byChain
|
|
76
|
+
->Array.map(((chainId, indices)) =>
|
|
77
|
+
` - chain ${chainId->Int.toString}: ${indices
|
|
78
|
+
->Array.map(index => index->Int.toString)
|
|
79
|
+
->Array.join(", ")}`
|
|
80
|
+
)
|
|
81
|
+
->Array.join("\n")
|
|
82
|
+
Some(
|
|
83
|
+
`simulate: ${count->Int.toString} ${itemWord} you passed to simulate never reached a handler, so nothing ran for them. Each was filtered out before the handler — usually a non-wildcard srcAddress that isn't indexed for the contract, or a where/block filter that excluded the event. Unrouted items, by index in each chain's simulate array:\n${lines}`,
|
|
84
|
+
)
|
|
85
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Utils from "./Utils.res.mjs";
|
|
4
|
+
import * as ChainMap from "./ChainMap.res.mjs";
|
|
5
|
+
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
6
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
7
|
+
|
|
8
|
+
function itemKey(item) {
|
|
9
|
+
if (item.kind === 0) {
|
|
10
|
+
return item.chain.toString() + `:` + item.blockNumber.toString() + `:` + item.logIndex.toString();
|
|
11
|
+
} else {
|
|
12
|
+
return "";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function makeFromConfig(config) {
|
|
17
|
+
let entries = ChainMap.values(config.chainMap).flatMap(chainConfig => {
|
|
18
|
+
let sources = chainConfig.sourceConfig;
|
|
19
|
+
if (sources.TAG === "CustomSources") {
|
|
20
|
+
return sources._0.flatMap(source => Stdlib_Option.getOr(source.simulateItems, []).map((item, index) => ({
|
|
21
|
+
chainId: chainConfig.id,
|
|
22
|
+
index: index,
|
|
23
|
+
key: itemKey(item)
|
|
24
|
+
})));
|
|
25
|
+
} else {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
if (entries.length !== 0) {
|
|
30
|
+
return {
|
|
31
|
+
unprocessed: entries
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function recordProcessed(t, batch) {
|
|
37
|
+
let processedKeys = new Set(batch.items.map(itemKey));
|
|
38
|
+
t.unprocessed = t.unprocessed.filter(entry => !processedKeys.has(entry.key));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function unroutedByChain(t) {
|
|
42
|
+
let indicesByChain = {};
|
|
43
|
+
let chainOrder = [];
|
|
44
|
+
t.unprocessed.forEach(entry => {
|
|
45
|
+
let key = entry.chainId.toString();
|
|
46
|
+
if (Stdlib_Option.isNone(indicesByChain[key])) {
|
|
47
|
+
chainOrder.push(entry.chainId);
|
|
48
|
+
}
|
|
49
|
+
Utils.Dict.push(indicesByChain, key, entry.index);
|
|
50
|
+
});
|
|
51
|
+
return chainOrder.map(chainId => [
|
|
52
|
+
chainId,
|
|
53
|
+
indicesByChain[chainId.toString()]
|
|
54
|
+
]);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function failureMessage(t) {
|
|
58
|
+
let byChain = unroutedByChain(t);
|
|
59
|
+
if (byChain.length === 0) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
let count = Stdlib_Array.reduce(byChain, 0, (acc, param) => acc + param[1].length | 0);
|
|
63
|
+
let itemWord = count === 1 ? "item" : "items";
|
|
64
|
+
let lines = byChain.map(param => ` - chain ` + param[0].toString() + `: ` + param[1].map(index => index.toString()).join(", ")).join("\n");
|
|
65
|
+
return `simulate: ` + count.toString() + ` ` + itemWord + ` you passed to simulate never reached a handler, so nothing ran for them. Each was filtered out before the handler — usually a non-wildcard srcAddress that isn't indexed for the contract, or a where/block filter that excluded the event. Unrouted items, by index in each chain's simulate array:\n` + lines;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export {
|
|
69
|
+
makeFromConfig,
|
|
70
|
+
recordProcessed,
|
|
71
|
+
failureMessage,
|
|
72
|
+
}
|
|
73
|
+
/* Utils Not a pure module */
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
type t
|
|
2
|
+
|
|
3
|
+
// Builds a tracker from the run's config, seeded with every item each simulate
|
|
4
|
+
// source provided. None when no chain has a simulate source.
|
|
5
|
+
let makeFromConfig: Config.t => option<t>
|
|
6
|
+
|
|
7
|
+
// Drop the batch's items from the not-yet-routed set, matched by event coordinate.
|
|
8
|
+
let recordProcessed: (t, ~batch: Batch.t) => unit
|
|
9
|
+
|
|
10
|
+
// The error to fail the run with when items never reached a handler, listing them
|
|
11
|
+
// by chain and index within that chain's `simulate` array. None when all routed.
|
|
12
|
+
let failureMessage: t => option<string>
|
package/src/SimulateItems.res
CHANGED
|
@@ -231,46 +231,6 @@ let deriveSrcAddress = (
|
|
|
231
231
|
}
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
-
// A non-wildcard event is gated by `clientAddressFilter` on srcAddress ownership,
|
|
235
|
-
// so a simulate item whose srcAddress isn't indexed on this chain would be
|
|
236
|
-
// silently dropped (handler never runs). Fail loudly instead.
|
|
237
|
-
//
|
|
238
|
-
// `config` must already have handler registrations applied, otherwise an event
|
|
239
|
-
// made wildcard purely through `indexer.onEvent({ wildcard: true })` reads back
|
|
240
|
-
// as non-wildcard and gets wrongly rejected.
|
|
241
|
-
let validateSrcAddresses = (
|
|
242
|
-
~simulateItems: array<JSON.t>,
|
|
243
|
-
~config: Config.t,
|
|
244
|
-
~chainConfig: Config.chain,
|
|
245
|
-
~indexingAddresses: array<Internal.indexingAddress>,
|
|
246
|
-
): unit => {
|
|
247
|
-
let known = Utils.Set.make()
|
|
248
|
-
indexingAddresses->Array.forEach(ia => known->Utils.Set.add(ia.address->Address.toString)->ignore)
|
|
249
|
-
simulateItems->Array.forEach(rawJson => {
|
|
250
|
-
let raw = rawJson->(Utils.magic: JSON.t => rawSimulateItem)
|
|
251
|
-
switch (raw->getContract, raw->getEvent) {
|
|
252
|
-
| (Some(contractName), Some(eventName)) =>
|
|
253
|
-
switch findEventConfig(~config, ~contractName, ~eventName) {
|
|
254
|
-
| Some(eventConfig) if !eventConfig.isWildcard =>
|
|
255
|
-
let item = rawJson->(Utils.magic: JSON.t => Envio.evmSimulateItem)
|
|
256
|
-
let srcAddress = deriveSrcAddress(
|
|
257
|
-
~providedSrcAddress=item.srcAddress,
|
|
258
|
-
~eventConfig,
|
|
259
|
-
~chainConfig,
|
|
260
|
-
)
|
|
261
|
-
if !(known->Utils.Set.has(srcAddress->Address.toString)) {
|
|
262
|
-
JsError.throwWithMessage(
|
|
263
|
-
`simulate: ${contractName}.${eventName} resolved to address ${srcAddress->Address.toString}, which isn't indexed on chain ${chainConfig.id->Int.toString}. ` ++
|
|
264
|
-
`Provide a "srcAddress" configured or registered for ${contractName} on this chain, or use a wildcard event.`,
|
|
265
|
-
)
|
|
266
|
-
}
|
|
267
|
-
| _ => ()
|
|
268
|
-
}
|
|
269
|
-
| _ => ()
|
|
270
|
-
}
|
|
271
|
-
})
|
|
272
|
-
}
|
|
273
|
-
|
|
274
234
|
let parse = (~simulateItems: array<JSON.t>, ~config: Config.t, ~chainConfig: Config.chain): array<
|
|
275
235
|
Internal.item,
|
|
276
236
|
> => {
|
|
@@ -281,8 +241,11 @@ let parse = (~simulateItems: array<JSON.t>, ~config: Config.t, ~chainConfig: Con
|
|
|
281
241
|
let currentLogIndex = ref(0)
|
|
282
242
|
|
|
283
243
|
let items = []
|
|
244
|
+
// Coordinate "block:logIndex" -> the index of the first item that claimed it,
|
|
245
|
+
// used to reject two items resolving to the same (block, logIndex).
|
|
246
|
+
let seenCoordinates = Dict.make()
|
|
284
247
|
|
|
285
|
-
simulateItems->Array.
|
|
248
|
+
simulateItems->Array.forEachWithIndex((rawJson, itemIndex) => {
|
|
286
249
|
let raw = rawJson->(Utils.magic: JSON.t => rawSimulateItem)
|
|
287
250
|
|
|
288
251
|
switch (raw->getContract, raw->getEvent) {
|
|
@@ -306,8 +269,14 @@ let parse = (~simulateItems: array<JSON.t>, ~config: Config.t, ~chainConfig: Con
|
|
|
306
269
|
}
|
|
307
270
|
let params = paramsJson->S.convertOrThrow(eventConfig.simulateParamsSchema)
|
|
308
271
|
|
|
272
|
+
// An explicit logIndex advances the auto-increment counter past itself, so a
|
|
273
|
+
// later item that omits logIndex picks up after it instead of colliding.
|
|
309
274
|
let logIndex = switch item.logIndex {
|
|
310
|
-
| Some(li) =>
|
|
275
|
+
| Some(li) =>
|
|
276
|
+
if li >= currentLogIndex.contents {
|
|
277
|
+
currentLogIndex := li + 1
|
|
278
|
+
}
|
|
279
|
+
li
|
|
311
280
|
| None =>
|
|
312
281
|
let li = currentLogIndex.contents
|
|
313
282
|
currentLogIndex := li + 1
|
|
@@ -345,6 +314,18 @@ let parse = (~simulateItems: array<JSON.t>, ~config: Config.t, ~chainConfig: Con
|
|
|
345
314
|
// Update currentBlock for subsequent items
|
|
346
315
|
currentBlock := blockNumber
|
|
347
316
|
|
|
317
|
+
// A simulate item must land on a distinct (block, logIndex): event ordering
|
|
318
|
+
// and the dead-input tracker both key on it. Catch explicit duplicates and
|
|
319
|
+
// explicit-vs-auto-increment collisions here, naming both offending indices.
|
|
320
|
+
let coordinate = `${blockNumber->Int.toString}:${logIndex->Int.toString}`
|
|
321
|
+
switch seenCoordinates->Dict.get(coordinate) {
|
|
322
|
+
| Some(firstIndex) =>
|
|
323
|
+
JsError.throwWithMessage(
|
|
324
|
+
`simulate: items at index ${firstIndex->Int.toString} and ${itemIndex->Int.toString} on chain ${chainId->Int.toString} both resolve to block ${blockNumber->Int.toString}, logIndex ${logIndex->Int.toString}. Give each item a distinct logIndex (or omit logIndex so they auto-increment).`,
|
|
325
|
+
)
|
|
326
|
+
| None => seenCoordinates->Dict.set(coordinate, itemIndex)
|
|
327
|
+
}
|
|
328
|
+
|
|
348
329
|
items
|
|
349
330
|
->Array.push(
|
|
350
331
|
Internal.Event({
|