envio 3.3.0-alpha.9 → 3.3.0-rc.0
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 +19 -0
- package/licenses/CLA.md +35 -0
- package/licenses/EULA.md +67 -0
- package/licenses/LICENSE.md +45 -0
- package/licenses/README.md +35 -0
- package/package.json +9 -8
- package/src/Batch.res.mjs +1 -1
- package/src/BatchProcessing.res +0 -7
- package/src/BatchProcessing.res.mjs +1 -8
- package/src/ChainFetching.res +30 -18
- package/src/ChainFetching.res.mjs +23 -13
- package/src/ChainState.res +134 -55
- package/src/ChainState.res.mjs +78 -36
- package/src/ChainState.resi +14 -2
- package/src/Config.res +9 -5
- package/src/Config.res.mjs +2 -2
- package/src/Core.res +20 -0
- package/src/Core.res.mjs +12 -0
- package/src/CrossChainState.res +115 -34
- package/src/CrossChainState.res.mjs +37 -14
- package/src/EffectState.res +100 -0
- package/src/EffectState.res.mjs +74 -0
- package/src/EffectState.resi +32 -0
- package/src/Envio.res +25 -7
- package/src/Envio.res.mjs +15 -19
- package/src/EventConfigBuilder.res +21 -46
- package/src/EventConfigBuilder.res.mjs +18 -25
- package/src/EventProcessing.res +8 -5
- package/src/EventProcessing.res.mjs +2 -1
- package/src/FetchState.res +814 -467
- package/src/FetchState.res.mjs +541 -369
- package/src/HandlerRegister.res +3 -1
- package/src/HandlerRegister.res.mjs +3 -2
- package/src/InMemoryStore.res +14 -26
- package/src/InMemoryStore.res.mjs +6 -19
- package/src/IndexerState.res +15 -39
- package/src/IndexerState.res.mjs +18 -29
- package/src/IndexerState.resi +2 -10
- package/src/Internal.res +98 -14
- package/src/Internal.res.mjs +96 -2
- package/src/LoadLayer.res +44 -24
- package/src/LoadLayer.res.mjs +43 -20
- package/src/LoadLayer.resi +1 -0
- package/src/LogSelection.res +0 -62
- package/src/LogSelection.res.mjs +0 -74
- package/src/Metrics.res +1 -1
- package/src/Metrics.res.mjs +1 -1
- package/src/Persistence.res +12 -3
- package/src/PgStorage.res +155 -82
- package/src/PgStorage.res.mjs +130 -77
- package/src/Prometheus.res +20 -10
- package/src/Prometheus.res.mjs +22 -10
- package/src/PruneStaleHistory.res +146 -35
- package/src/PruneStaleHistory.res.mjs +114 -20
- package/src/RawEvent.res +2 -2
- package/src/Rollback.res +32 -13
- package/src/Rollback.res.mjs +24 -11
- package/src/SimulateDeadInputTracker.res +1 -1
- package/src/SimulateItems.res +38 -6
- package/src/SimulateItems.res.mjs +28 -9
- package/src/TestIndexer.res +2 -2
- package/src/TestIndexer.res.mjs +2 -2
- package/src/TopicFilter.res +1 -25
- package/src/TopicFilter.res.mjs +0 -74
- package/src/UserContext.res +62 -23
- package/src/UserContext.res.mjs +26 -6
- package/src/Writing.res +60 -21
- package/src/Writing.res.mjs +27 -9
- package/src/bindings/NodeJs.res +5 -0
- package/src/bindings/Viem.res +0 -5
- package/src/sources/EventRouter.res +0 -21
- package/src/sources/EventRouter.res.mjs +0 -12
- package/src/sources/EvmChain.res +2 -27
- package/src/sources/EvmChain.res.mjs +2 -23
- package/src/sources/EvmRpcClient.res +58 -23
- package/src/sources/EvmRpcClient.res.mjs +11 -5
- package/src/sources/HyperSync.res +9 -38
- package/src/sources/HyperSync.res.mjs +16 -28
- package/src/sources/HyperSync.resi +2 -2
- package/src/sources/HyperSyncClient.res +88 -11
- package/src/sources/HyperSyncClient.res.mjs +39 -6
- package/src/sources/HyperSyncSource.res +18 -199
- package/src/sources/HyperSyncSource.res.mjs +9 -128
- package/src/sources/Rpc.res +0 -32
- package/src/sources/Rpc.res.mjs +1 -46
- package/src/sources/RpcSource.res +129 -522
- package/src/sources/RpcSource.res.mjs +161 -366
- package/src/sources/SimulateSource.res +37 -19
- package/src/sources/SimulateSource.res.mjs +27 -10
- package/src/sources/SourceManager.res +3 -7
- package/src/sources/SourceManager.res.mjs +2 -7
- package/src/sources/SourceManager.resi +0 -2
- package/src/sources/SvmHyperSyncSource.res +13 -3
- package/src/sources/SvmHyperSyncSource.res.mjs +1 -1
package/src/ChainState.res
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
type t = {
|
|
6
6
|
logger: Pino.t,
|
|
7
|
+
// The registrations used to build this chain's sources and route native items.
|
|
8
|
+
onEventRegistrations: array<Internal.onEventRegistration>,
|
|
7
9
|
mutable fetchState: FetchState.t,
|
|
8
10
|
// The chain-wide address index. Not `mutable`: the dict is mutated in place by
|
|
9
11
|
// register/rollback, so the reference is stable across fetchState versions.
|
|
@@ -13,8 +15,13 @@ type t = {
|
|
|
13
15
|
mutable isProgressAtHead: bool,
|
|
14
16
|
mutable timestampCaughtUpToHeadOrEndblock: option<Date.t>,
|
|
15
17
|
mutable committedProgressBlockNumber: int,
|
|
18
|
+
// Progress of the batch currently being processed. The buffer is consumed at
|
|
19
|
+
// batch creation (see advanceAfterBatch), so this runs ahead of
|
|
20
|
+
// committedProgressBlockNumber until the batch commits — it's the true lower
|
|
21
|
+
// boundary of the remaining buffer's block span.
|
|
22
|
+
mutable processingBlockNumber: int,
|
|
16
23
|
mutable numEventsProcessed: float,
|
|
17
|
-
// Running sum of in-flight queries'
|
|
24
|
+
// Running sum of in-flight queries' itemsEst, kept here so the
|
|
18
25
|
// scheduler doesn't re-sum pending queries on every tick. Incremented when
|
|
19
26
|
// queries are dispatched, decremented as their responses land.
|
|
20
27
|
mutable pendingBudget: float,
|
|
@@ -66,9 +73,22 @@ let configAddresses = (chainConfig: Config.chain): array<Internal.indexingAddres
|
|
|
66
73
|
addresses
|
|
67
74
|
}
|
|
68
75
|
|
|
76
|
+
let validateOnEventRegistrations = (
|
|
77
|
+
~chainId: int,
|
|
78
|
+
registrations: array<Internal.onEventRegistration>,
|
|
79
|
+
) =>
|
|
80
|
+
registrations->Array.forEachWithIndex((registration, expectedIndex) => {
|
|
81
|
+
if registration.index !== expectedIndex {
|
|
82
|
+
JsError.throwWithMessage(
|
|
83
|
+
`Invalid onEvent registration index for chain ${chainId->Int.toString}: ${registration.eventConfig.contractName}.${registration.eventConfig.name} has index ${registration.index->Int.toString}, but its ChainState position is ${expectedIndex->Int.toString}.`,
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
|
|
69
88
|
let make = (
|
|
70
89
|
~chainConfig: Config.chain,
|
|
71
90
|
~fetchState: FetchState.t,
|
|
91
|
+
~onEventRegistrations=[],
|
|
72
92
|
~indexingAddresses: IndexingAddresses.t,
|
|
73
93
|
~sourceManager: SourceManager.t,
|
|
74
94
|
~reorgDetection: ReorgDetection.t,
|
|
@@ -82,21 +102,26 @@ let make = (
|
|
|
82
102
|
~blockStore=BlockStore.make(~ecosystem=Ecosystem.Evm, ~shouldChecksum=false),
|
|
83
103
|
~logger: Pino.t,
|
|
84
104
|
): t => {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
105
|
+
validateOnEventRegistrations(~chainId=chainConfig.id, onEventRegistrations)
|
|
106
|
+
{
|
|
107
|
+
logger,
|
|
108
|
+
onEventRegistrations,
|
|
109
|
+
fetchState,
|
|
110
|
+
indexingAddresses,
|
|
111
|
+
sourceManager,
|
|
112
|
+
chainConfig,
|
|
113
|
+
isProgressAtHead,
|
|
114
|
+
timestampCaughtUpToHeadOrEndblock,
|
|
115
|
+
committedProgressBlockNumber,
|
|
116
|
+
processingBlockNumber: committedProgressBlockNumber,
|
|
117
|
+
numEventsProcessed,
|
|
118
|
+
pendingBudget: 0.,
|
|
119
|
+
chainDensity,
|
|
120
|
+
reorgDetection,
|
|
121
|
+
safeCheckpointTracking,
|
|
122
|
+
transactionStore,
|
|
123
|
+
blockStore,
|
|
124
|
+
}
|
|
100
125
|
}
|
|
101
126
|
|
|
102
127
|
let makeInternal = (
|
|
@@ -232,6 +257,7 @@ let makeInternal = (
|
|
|
232
257
|
make(
|
|
233
258
|
~chainConfig,
|
|
234
259
|
~fetchState,
|
|
260
|
+
~onEventRegistrations,
|
|
235
261
|
~indexingAddresses=indexingAddressIndex,
|
|
236
262
|
~sourceManager=SourceManager.make(~sources, ~isRealtime, ~reducedPollingInterval?),
|
|
237
263
|
~reorgDetection=ReorgDetection.make(
|
|
@@ -367,7 +393,7 @@ let startFetchingQueries = (cs: t, ~queries: array<FetchState.query>) => {
|
|
|
367
393
|
cs.fetchState->FetchState.startFetchingQueries(~queries)
|
|
368
394
|
cs.pendingBudget =
|
|
369
395
|
cs.pendingBudget +.
|
|
370
|
-
queries->Array.reduce(0., (acc, query) => acc +. query.
|
|
396
|
+
queries->Array.reduce(0., (acc, query) => acc +. query.itemsEst->Int.toFloat)
|
|
371
397
|
}
|
|
372
398
|
|
|
373
399
|
// Drop every in-flight query and release their reservations together, keeping
|
|
@@ -384,44 +410,74 @@ let isReady = (cs: t) => cs.timestampCaughtUpToHeadOrEndblock !== None
|
|
|
384
410
|
// nudge it.
|
|
385
411
|
let densityBlendWindow = 100.
|
|
386
412
|
|
|
387
|
-
// The last block this chain can fetch right now: the head
|
|
388
|
-
//
|
|
413
|
+
// The last block this chain can fetch right now: the lagged head
|
|
414
|
+
// (knownHeight - blockLag, the frontier when a reorg-threshold blockLag holds
|
|
415
|
+
// back the tip), or endBlock when it's below that head. Matching isFetchingAtHead's
|
|
416
|
+
// definition of the head is what lets a chain parked at its lagged head read
|
|
417
|
+
// 100% progress instead of looking behind against blocks it can't fetch.
|
|
389
418
|
let fetchCeiling = (cs: t) => {
|
|
390
419
|
let fetchState = cs.fetchState
|
|
420
|
+
let head = Pervasives.max(0, fetchState.knownHeight - fetchState.blockLag)
|
|
391
421
|
switch fetchState.endBlock {
|
|
392
|
-
| Some(endBlock) => Pervasives.min(endBlock,
|
|
393
|
-
| None =>
|
|
422
|
+
| Some(endBlock) => Pervasives.min(endBlock, head)
|
|
423
|
+
| None => head
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Events/block over the ready part of the buffer — a live signal that reacts
|
|
428
|
+
// to what fetching just found, unlike the processing EMA which only moves as
|
|
429
|
+
// batches commit.
|
|
430
|
+
let readyBufferDensity = (cs: t) => {
|
|
431
|
+
let readyCount = cs.fetchState->FetchState.bufferReadyCount
|
|
432
|
+
let span = cs.fetchState->FetchState.bufferBlockNumber - cs.processingBlockNumber
|
|
433
|
+
if readyCount > 0 && span > 0 {
|
|
434
|
+
Some(readyCount->Int.toFloat /. span->Int.toFloat)
|
|
435
|
+
} else {
|
|
436
|
+
None
|
|
394
437
|
}
|
|
395
438
|
}
|
|
396
439
|
|
|
440
|
+
// Density used for query sizing: the higher of the processing EMA and the
|
|
441
|
+
// ready-buffer density, so a stale-low EMA can't undersize queries while the
|
|
442
|
+
// buffer proves the range is dense. None means the chain is cold — no density
|
|
443
|
+
// signal at all.
|
|
444
|
+
let effectiveDensity = (cs: t) =>
|
|
445
|
+
switch (cs.chainDensity, cs->readyBufferDensity) {
|
|
446
|
+
| (Some(ema), Some(buffer)) => Some(Pervasives.max(ema, buffer))
|
|
447
|
+
| (Some(_) as density, None) | (None, Some(_) as density) => density
|
|
448
|
+
| (None, None) => None
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// How far past the frontier a chain with no density signal targets while it
|
|
452
|
+
// takes its first measurements.
|
|
453
|
+
let coldTargetRange = 20_000
|
|
454
|
+
|
|
397
455
|
// This chain's share of the indexer-wide buffer budget turned into a soft
|
|
398
|
-
// target block: via
|
|
399
|
-
//
|
|
456
|
+
// target block: via density, or frontier + coldTargetRange when there's no
|
|
457
|
+
// density signal yet.
|
|
400
458
|
let targetBlock = (cs: t, ~chainTargetItems: float) => {
|
|
401
459
|
let fetchState = cs.fetchState
|
|
402
460
|
let fetchCeiling = cs->fetchCeiling
|
|
403
461
|
let bufferBlockNumber = fetchState->FetchState.bufferBlockNumber
|
|
404
|
-
switch cs
|
|
462
|
+
switch cs->effectiveDensity {
|
|
405
463
|
| Some(density) if density > 0. =>
|
|
406
464
|
Pervasives.min(
|
|
407
465
|
fetchCeiling,
|
|
408
466
|
bufferBlockNumber + Math.ceil(chainTargetItems /. density)->Float.toInt,
|
|
409
467
|
)
|
|
410
|
-
| _ => fetchCeiling
|
|
468
|
+
| _ => Pervasives.min(bufferBlockNumber + coldTargetRange, fetchCeiling)
|
|
411
469
|
}
|
|
412
470
|
}
|
|
413
471
|
|
|
414
472
|
// Block range that cross-chain progress alignment maps fractions over: from
|
|
415
|
-
// the
|
|
416
|
-
//
|
|
473
|
+
// the chain's startBlock to the last block it can fetch right now. The lower
|
|
474
|
+
// bound is deliberately static — anchoring it at firstEventBlock would make
|
|
475
|
+
// two chains sitting at the same block read different progress fractions
|
|
476
|
+
// depending on whether each has discovered its first event yet, so the
|
|
477
|
+
// anchor's line could map below a follower's own frontier and stall it.
|
|
417
478
|
let progressRange = (cs: t) => {
|
|
418
479
|
let fetchState = cs.fetchState
|
|
419
|
-
|
|
420
|
-
let upper = switch fetchState.endBlock {
|
|
421
|
-
| Some(endBlock) => endBlock
|
|
422
|
-
| None => fetchState.knownHeight
|
|
423
|
-
}
|
|
424
|
-
(lower, upper)
|
|
480
|
+
(fetchState.startBlock, cs->fetchCeiling)
|
|
425
481
|
}
|
|
426
482
|
|
|
427
483
|
// A degenerate range (chain already at or past its last block) maps to 1 so it
|
|
@@ -442,13 +498,25 @@ let blockAtProgress = (cs: t, ~progress) => {
|
|
|
442
498
|
lower + Math.ceil(progress *. (upper - lower)->Int.toFloat)->Float.toInt
|
|
443
499
|
}
|
|
444
500
|
|
|
501
|
+
// The fetch frontier as a fraction of the alignable range — what the
|
|
502
|
+
// cross-chain waterfall aligns other chains against. Based on blocks actually
|
|
503
|
+
// fetched, so it holds up even while this chain's queries are in flight.
|
|
504
|
+
let frontierProgress = (cs: t) =>
|
|
505
|
+
cs->progressAtBlock(~blockNumber=cs.fetchState->FetchState.bufferBlockNumber)
|
|
506
|
+
|
|
445
507
|
// Propose queries sized against this chain's target block. Called by
|
|
446
508
|
// CrossChainState's waterfall, furthest-behind chain first, with
|
|
447
509
|
// chainTargetItems set to whatever budget remains at that point and
|
|
448
510
|
// maxTargetBlock set to the most-behind chain's progress mapped onto this
|
|
449
511
|
// chain, so a chain with budget can't run further ahead than the chain the
|
|
450
512
|
// whole pool is prioritizing.
|
|
451
|
-
let getNextQuery = (
|
|
513
|
+
let getNextQuery = (
|
|
514
|
+
cs: t,
|
|
515
|
+
~chainTargetItems: float,
|
|
516
|
+
~chunkItemsMultiplier=1.,
|
|
517
|
+
~itemsTargetFloor=0,
|
|
518
|
+
~maxTargetBlock=?,
|
|
519
|
+
) => {
|
|
452
520
|
let chainTargetBlock = cs->targetBlock(~chainTargetItems)
|
|
453
521
|
let chainTargetBlock = switch maxTargetBlock {
|
|
454
522
|
| Some(maxTargetBlock) => Pervasives.min(chainTargetBlock, maxTargetBlock)
|
|
@@ -460,25 +528,25 @@ let getNextQuery = (cs: t, ~chainTargetItems: float, ~maxTargetBlock=?) => {
|
|
|
460
528
|
// top: they're already accounted and shouldn't crowd out new partitions), so
|
|
461
529
|
// the waterfall's leftover flows to the next chain in the same tick instead
|
|
462
530
|
// of being held by an oversized probe.
|
|
463
|
-
let chainTargetItems = switch cs
|
|
531
|
+
let chainTargetItems = switch cs->effectiveDensity {
|
|
464
532
|
| Some(density) if density > 0. =>
|
|
533
|
+
// No extra headroom here: the budget is reserved in honest itemsEst units,
|
|
534
|
+
// and truncation safety lives in the itemsTarget server cap (sized with
|
|
535
|
+
// chunkItemsMultiplier at query creation) — multiplying the budget cap too
|
|
536
|
+
// would compound the two and hold budget away from other chains.
|
|
465
537
|
let rangeCost =
|
|
466
538
|
density *. (chainTargetBlock - cs.fetchState->FetchState.bufferBlockNumber)->Int.toFloat
|
|
467
|
-
// 3x headroom only for a chain already caught up once and polling the
|
|
468
|
-
// head: there a single query covers the whole remaining range, and a
|
|
469
|
-
// slightly denser-than-expected range would otherwise truncate at the
|
|
470
|
-
// server cap and force an immediate catch-up query for the last blocks.
|
|
471
|
-
// During backfill the range never fits one query anyway, so headroom
|
|
472
|
-
// would just hold budget away from other chains.
|
|
473
|
-
let rangeCost =
|
|
474
|
-
chainTargetBlock >= cs->fetchCeiling && cs->isReady ? rangeCost *. 3. : rangeCost
|
|
475
539
|
Pervasives.min(chainTargetItems, Math.ceil(rangeCost) +. cs.pendingBudget)
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
Pervasives.min(chainTargetItems, 5_000. +. cs.pendingBudget)
|
|
540
|
+
// No density signal: the cross-chain waterfall already clamped the handed
|
|
541
|
+
// budget to the cold-chain cap, so it's used as-is.
|
|
542
|
+
| _ => chainTargetItems
|
|
480
543
|
}
|
|
481
|
-
cs.fetchState->FetchState.getNextQuery(
|
|
544
|
+
cs.fetchState->FetchState.getNextQuery(
|
|
545
|
+
~chainTargetBlock,
|
|
546
|
+
~chainTargetItems,
|
|
547
|
+
~chunkItemsMultiplier,
|
|
548
|
+
~itemsTargetFloor,
|
|
549
|
+
)
|
|
482
550
|
}
|
|
483
551
|
|
|
484
552
|
// Run a fetch tick for this chain against its sources, feeding the owned fetch
|
|
@@ -712,6 +780,9 @@ let materializePageItems = async (
|
|
|
712
780
|
))
|
|
713
781
|
}
|
|
714
782
|
|
|
783
|
+
let filterByClientAddress = (cs: t, items: array<Internal.item>): array<Internal.item> =>
|
|
784
|
+
items->FetchState.filterByClientAddress(~indexingAddresses=cs.indexingAddresses)
|
|
785
|
+
|
|
715
786
|
let handleQueryResult = (
|
|
716
787
|
cs: t,
|
|
717
788
|
~query: FetchState.query,
|
|
@@ -744,16 +815,11 @@ let handleQueryResult = (
|
|
|
744
815
|
|
|
745
816
|
cs.fetchState =
|
|
746
817
|
fs
|
|
747
|
-
->FetchState.handleQueryResult(
|
|
748
|
-
~indexingAddresses=cs.indexingAddresses,
|
|
749
|
-
~query,
|
|
750
|
-
~latestFetchedBlock,
|
|
751
|
-
~newItems,
|
|
752
|
-
)
|
|
818
|
+
->FetchState.handleQueryResult(~query, ~latestFetchedBlock, ~newItems)
|
|
753
819
|
->FetchState.updateKnownHeight(~knownHeight)
|
|
754
820
|
|
|
755
821
|
// The query is no longer in flight, so release its reservation.
|
|
756
|
-
cs.pendingBudget = Pervasives.max(0., cs.pendingBudget -. query.
|
|
822
|
+
cs.pendingBudget = Pervasives.max(0., cs.pendingBudget -. query.itemsEst->Int.toFloat)
|
|
757
823
|
}
|
|
758
824
|
|
|
759
825
|
// Run reorg detection against a fetch response and commit the updated guard.
|
|
@@ -854,6 +920,10 @@ let advanceAfterBatch = (cs: t, ~batch: Batch.t, ~enteringReorgThreshold) =>
|
|
|
854
920
|
cs.fetchState = enteringReorgThreshold
|
|
855
921
|
? chainAfterBatch.fetchState->FetchState.updateInternal(~blockLag=cs.chainConfig.blockLag)
|
|
856
922
|
: chainAfterBatch.fetchState
|
|
923
|
+
|
|
924
|
+
// The batch's items just left the buffer, so the remaining buffer's span
|
|
925
|
+
// starts at the batch's progress.
|
|
926
|
+
cs.processingBlockNumber = chainAfterBatch.progressBlockNumber
|
|
857
927
|
| None => ()
|
|
858
928
|
}
|
|
859
929
|
|
|
@@ -940,6 +1010,13 @@ let applyBatchProgress = (cs: t, ~batch: Batch.t, ~blockTimestampName: string) =
|
|
|
940
1010
|
}
|
|
941
1011
|
|
|
942
1012
|
cs.committedProgressBlockNumber = chainAfterBatch.progressBlockNumber
|
|
1013
|
+
|
|
1014
|
+
// Normally already set by advanceAfterBatch at batch creation; catch up
|
|
1015
|
+
// here for paths that commit progress without it.
|
|
1016
|
+
cs.processingBlockNumber = Pervasives.max(
|
|
1017
|
+
cs.processingBlockNumber,
|
|
1018
|
+
chainAfterBatch.progressBlockNumber,
|
|
1019
|
+
)
|
|
943
1020
|
cs.numEventsProcessed = chainAfterBatch.totalEventsProcessed
|
|
944
1021
|
// Processed blocks' transactions and blocks are no longer needed.
|
|
945
1022
|
cs.transactionStore->TransactionStore.prune(chainAfterBatch.progressBlockNumber)
|
|
@@ -1024,6 +1101,7 @@ let rollback = (
|
|
|
1024
1101
|
cs.transactionStore->TransactionStore.rollback(newProgressBlockNumber)
|
|
1025
1102
|
cs.blockStore->BlockStore.rollback(newProgressBlockNumber)
|
|
1026
1103
|
cs.committedProgressBlockNumber = newProgressBlockNumber
|
|
1104
|
+
cs.processingBlockNumber = newProgressBlockNumber
|
|
1027
1105
|
cs.numEventsProcessed = newTotalEventsProcessed
|
|
1028
1106
|
| None =>
|
|
1029
1107
|
if isReorgChain {
|
|
@@ -1042,6 +1120,7 @@ let rollback = (
|
|
|
1042
1120
|
cs.committedProgressBlockNumber,
|
|
1043
1121
|
rollbackTargetBlockNumber,
|
|
1044
1122
|
)
|
|
1123
|
+
cs.processingBlockNumber = Pervasives.min(cs.processingBlockNumber, rollbackTargetBlockNumber)
|
|
1045
1124
|
}
|
|
1046
1125
|
}
|
|
1047
1126
|
}
|
package/src/ChainState.res.mjs
CHANGED
|
@@ -40,7 +40,16 @@ function configAddresses(chainConfig) {
|
|
|
40
40
|
return addresses;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
function
|
|
43
|
+
function validateOnEventRegistrations(chainId, registrations) {
|
|
44
|
+
registrations.forEach((registration, expectedIndex) => {
|
|
45
|
+
if (registration.index !== expectedIndex) {
|
|
46
|
+
return Stdlib_JsError.throwWithMessage(`Invalid onEvent registration index for chain ` + chainId.toString() + `: ` + registration.eventConfig.contractName + `.` + registration.eventConfig.name + ` has index ` + registration.index.toString() + `, but its ChainState position is ` + expectedIndex.toString() + `.`);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function make(chainConfig, fetchState, onEventRegistrationsOpt, indexingAddresses, sourceManager, reorgDetection, committedProgressBlockNumber, safeCheckpointTrackingOpt, numEventsProcessedOpt, timestampCaughtUpToHeadOrEndblockOpt, isProgressAtHeadOpt, transactionStoreOpt, chainDensityOpt, blockStoreOpt, logger) {
|
|
52
|
+
let onEventRegistrations = onEventRegistrationsOpt !== undefined ? onEventRegistrationsOpt : [];
|
|
44
53
|
let safeCheckpointTracking = safeCheckpointTrackingOpt !== undefined ? Primitive_option.valFromOption(safeCheckpointTrackingOpt) : undefined;
|
|
45
54
|
let numEventsProcessed = numEventsProcessedOpt !== undefined ? numEventsProcessedOpt : 0;
|
|
46
55
|
let timestampCaughtUpToHeadOrEndblock = timestampCaughtUpToHeadOrEndblockOpt !== undefined ? Primitive_option.valFromOption(timestampCaughtUpToHeadOrEndblockOpt) : undefined;
|
|
@@ -48,8 +57,10 @@ function make(chainConfig, fetchState, indexingAddresses, sourceManager, reorgDe
|
|
|
48
57
|
let transactionStore = transactionStoreOpt !== undefined ? Primitive_option.valFromOption(transactionStoreOpt) : TransactionStore.make("evm", false);
|
|
49
58
|
let chainDensity = chainDensityOpt !== undefined ? Primitive_option.valFromOption(chainDensityOpt) : undefined;
|
|
50
59
|
let blockStore = blockStoreOpt !== undefined ? Primitive_option.valFromOption(blockStoreOpt) : BlockStore.make("evm", false);
|
|
60
|
+
validateOnEventRegistrations(chainConfig.id, onEventRegistrations);
|
|
51
61
|
return {
|
|
52
62
|
logger: logger,
|
|
63
|
+
onEventRegistrations: onEventRegistrations,
|
|
53
64
|
fetchState: fetchState,
|
|
54
65
|
indexingAddresses: indexingAddresses,
|
|
55
66
|
sourceManager: sourceManager,
|
|
@@ -57,6 +68,7 @@ function make(chainConfig, fetchState, indexingAddresses, sourceManager, reorgDe
|
|
|
57
68
|
isProgressAtHead: isProgressAtHead,
|
|
58
69
|
timestampCaughtUpToHeadOrEndblock: timestampCaughtUpToHeadOrEndblock,
|
|
59
70
|
committedProgressBlockNumber: committedProgressBlockNumber,
|
|
71
|
+
processingBlockNumber: committedProgressBlockNumber,
|
|
60
72
|
numEventsProcessed: numEventsProcessed,
|
|
61
73
|
pendingBudget: 0,
|
|
62
74
|
chainDensity: chainDensity,
|
|
@@ -135,7 +147,7 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
|
|
|
135
147
|
}
|
|
136
148
|
let firstEventBlock$1 = fetchState.firstEventBlock;
|
|
137
149
|
let chainDensity = firstEventBlock$1 !== undefined && progressBlockNumber > firstEventBlock$1 && numEventsProcessed > 0 ? numEventsProcessed / (progressBlockNumber - firstEventBlock$1 | 0) : undefined;
|
|
138
|
-
return make(chainConfig, fetchState, indexingAddressIndex, SourceManager.make(sources$1, isRealtime, undefined, undefined, undefined, reducedPollingInterval, undefined, undefined), ReorgDetection.make(chainReorgCheckpoints, maxReorgDepth, config.shouldRollbackOnReorg), progressBlockNumber, Primitive_option.some(SafeCheckpointTracking.make(maxReorgDepth, config.shouldRollbackOnReorg, chainReorgCheckpoints)), numEventsProcessed, Primitive_option.some(timestampCaughtUpToHeadOrEndblock), undefined, Primitive_option.some(TransactionStore.make(config.ecosystem.name, !lowercaseAddresses)), Primitive_option.some(chainDensity), Primitive_option.some(BlockStore.make(config.ecosystem.name, !lowercaseAddresses)), logger);
|
|
150
|
+
return make(chainConfig, fetchState, onEventRegistrations, indexingAddressIndex, SourceManager.make(sources$1, isRealtime, undefined, undefined, undefined, reducedPollingInterval, undefined, undefined), ReorgDetection.make(chainReorgCheckpoints, maxReorgDepth, config.shouldRollbackOnReorg), progressBlockNumber, Primitive_option.some(SafeCheckpointTracking.make(maxReorgDepth, config.shouldRollbackOnReorg, chainReorgCheckpoints)), numEventsProcessed, Primitive_option.some(timestampCaughtUpToHeadOrEndblock), undefined, Primitive_option.some(TransactionStore.make(config.ecosystem.name, !lowercaseAddresses)), Primitive_option.some(chainDensity), Primitive_option.some(BlockStore.make(config.ecosystem.name, !lowercaseAddresses)), logger);
|
|
139
151
|
}
|
|
140
152
|
|
|
141
153
|
function makeFromConfig(chainConfig, config, registrationsByChainId, knownHeight) {
|
|
@@ -233,7 +245,7 @@ function isReadyToEnterReorgThreshold(cs) {
|
|
|
233
245
|
|
|
234
246
|
function startFetchingQueries(cs, queries) {
|
|
235
247
|
FetchState.startFetchingQueries(cs.fetchState, queries);
|
|
236
|
-
cs.pendingBudget = cs.pendingBudget + Stdlib_Array.reduce(queries, 0, (acc, query) => acc + query.
|
|
248
|
+
cs.pendingBudget = cs.pendingBudget + Stdlib_Array.reduce(queries, 0, (acc, query) => acc + query.itemsEst);
|
|
237
249
|
}
|
|
238
250
|
|
|
239
251
|
function isReady(cs) {
|
|
@@ -242,11 +254,36 @@ function isReady(cs) {
|
|
|
242
254
|
|
|
243
255
|
function fetchCeiling(cs) {
|
|
244
256
|
let fetchState = cs.fetchState;
|
|
257
|
+
let head = Primitive_int.max(0, fetchState.knownHeight - fetchState.blockLag | 0);
|
|
245
258
|
let endBlock = fetchState.endBlock;
|
|
246
259
|
if (endBlock !== undefined) {
|
|
247
|
-
return Primitive_int.min(endBlock,
|
|
260
|
+
return Primitive_int.min(endBlock, head);
|
|
261
|
+
} else {
|
|
262
|
+
return head;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function readyBufferDensity(cs) {
|
|
267
|
+
let readyCount = FetchState.bufferReadyCount(cs.fetchState);
|
|
268
|
+
let span = FetchState.bufferBlockNumber(cs.fetchState) - cs.processingBlockNumber | 0;
|
|
269
|
+
if (readyCount > 0 && span > 0) {
|
|
270
|
+
return readyCount / span;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function effectiveDensity(cs) {
|
|
275
|
+
let match = cs.chainDensity;
|
|
276
|
+
let match$1 = readyBufferDensity(cs);
|
|
277
|
+
if (match !== undefined) {
|
|
278
|
+
if (match$1 !== undefined) {
|
|
279
|
+
return Primitive_float.max(match, match$1);
|
|
280
|
+
} else {
|
|
281
|
+
return match;
|
|
282
|
+
}
|
|
283
|
+
} else if (match$1 !== undefined) {
|
|
284
|
+
return match$1;
|
|
248
285
|
} else {
|
|
249
|
-
return
|
|
286
|
+
return;
|
|
250
287
|
}
|
|
251
288
|
}
|
|
252
289
|
|
|
@@ -254,26 +291,30 @@ function targetBlock(cs, chainTargetItems) {
|
|
|
254
291
|
let fetchState = cs.fetchState;
|
|
255
292
|
let fetchCeiling$1 = fetchCeiling(cs);
|
|
256
293
|
let bufferBlockNumber = FetchState.bufferBlockNumber(fetchState);
|
|
257
|
-
let density = cs
|
|
294
|
+
let density = effectiveDensity(cs);
|
|
258
295
|
if (density !== undefined && density > 0) {
|
|
259
296
|
return Primitive_int.min(fetchCeiling$1, bufferBlockNumber + (Math.ceil(chainTargetItems / density) | 0) | 0);
|
|
260
297
|
} else {
|
|
261
|
-
return fetchCeiling$1;
|
|
298
|
+
return Primitive_int.min(bufferBlockNumber + 20000 | 0, fetchCeiling$1);
|
|
262
299
|
}
|
|
263
300
|
}
|
|
264
301
|
|
|
265
302
|
function progressRange(cs) {
|
|
266
303
|
let fetchState = cs.fetchState;
|
|
267
|
-
let lower = Stdlib_Option.getOr(fetchState.firstEventBlock, fetchState.startBlock);
|
|
268
|
-
let endBlock = fetchState.endBlock;
|
|
269
|
-
let upper = endBlock !== undefined ? endBlock : fetchState.knownHeight;
|
|
270
304
|
return [
|
|
271
|
-
|
|
272
|
-
|
|
305
|
+
fetchState.startBlock,
|
|
306
|
+
fetchCeiling(cs)
|
|
273
307
|
];
|
|
274
308
|
}
|
|
275
309
|
|
|
276
|
-
function
|
|
310
|
+
function blockAtProgress(cs, progress) {
|
|
311
|
+
let match = progressRange(cs);
|
|
312
|
+
let lower = match[0];
|
|
313
|
+
return lower + (Math.ceil(progress * (match[1] - lower | 0)) | 0) | 0;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function frontierProgress(cs) {
|
|
317
|
+
let blockNumber = FetchState.bufferBlockNumber(cs.fetchState);
|
|
277
318
|
let match = progressRange(cs);
|
|
278
319
|
let upper = match[1];
|
|
279
320
|
let lower = match[0];
|
|
@@ -284,29 +325,20 @@ function progressAtBlock(cs, blockNumber) {
|
|
|
284
325
|
}
|
|
285
326
|
}
|
|
286
327
|
|
|
287
|
-
function
|
|
288
|
-
let
|
|
289
|
-
let
|
|
290
|
-
return lower + (Math.ceil(progress * (match[1] - lower | 0)) | 0) | 0;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
function getNextQuery(cs, chainTargetItems, maxTargetBlock) {
|
|
328
|
+
function getNextQuery(cs, chainTargetItems, chunkItemsMultiplierOpt, itemsTargetFloorOpt, maxTargetBlock) {
|
|
329
|
+
let chunkItemsMultiplier = chunkItemsMultiplierOpt !== undefined ? chunkItemsMultiplierOpt : 1;
|
|
330
|
+
let itemsTargetFloor = itemsTargetFloorOpt !== undefined ? itemsTargetFloorOpt : 0;
|
|
294
331
|
let chainTargetBlock = targetBlock(cs, chainTargetItems);
|
|
295
332
|
let chainTargetBlock$1 = maxTargetBlock !== undefined ? Primitive_int.min(chainTargetBlock, maxTargetBlock) : chainTargetBlock;
|
|
296
|
-
let density = cs
|
|
333
|
+
let density = effectiveDensity(cs);
|
|
297
334
|
let chainTargetItems$1;
|
|
298
|
-
if (density !== undefined) {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
let rangeCost$1 = chainTargetBlock$1 >= fetchCeiling(cs) && cs.timestampCaughtUpToHeadOrEndblock !== undefined ? rangeCost * 3 : rangeCost;
|
|
302
|
-
chainTargetItems$1 = Primitive_float.min(chainTargetItems, Math.ceil(rangeCost$1) + cs.pendingBudget);
|
|
303
|
-
} else {
|
|
304
|
-
chainTargetItems$1 = Primitive_float.min(chainTargetItems, 5000 + cs.pendingBudget);
|
|
305
|
-
}
|
|
335
|
+
if (density !== undefined && density > 0) {
|
|
336
|
+
let rangeCost = density * (chainTargetBlock$1 - FetchState.bufferBlockNumber(cs.fetchState) | 0);
|
|
337
|
+
chainTargetItems$1 = Primitive_float.min(chainTargetItems, Math.ceil(rangeCost) + cs.pendingBudget);
|
|
306
338
|
} else {
|
|
307
|
-
chainTargetItems$1 =
|
|
339
|
+
chainTargetItems$1 = chainTargetItems;
|
|
308
340
|
}
|
|
309
|
-
return FetchState.getNextQuery(cs.fetchState, chainTargetBlock$1, chainTargetItems$1);
|
|
341
|
+
return FetchState.getNextQuery(cs.fetchState, chainTargetBlock$1, chainTargetItems$1, chunkItemsMultiplier, itemsTargetFloor);
|
|
310
342
|
}
|
|
311
343
|
|
|
312
344
|
function dispatch(cs, executeQuery, waitForNewBlock, onNewBlock, action, stateId) {
|
|
@@ -477,6 +509,10 @@ async function materializePageItems(items, transactionStore, blockStore, ecosyst
|
|
|
477
509
|
]);
|
|
478
510
|
}
|
|
479
511
|
|
|
512
|
+
function filterByClientAddress(cs, items) {
|
|
513
|
+
return FetchState.filterByClientAddress(items, cs.indexingAddresses);
|
|
514
|
+
}
|
|
515
|
+
|
|
480
516
|
function handleQueryResult(cs, query, newItems, newItemsWithDcs, latestFetchedBlock, knownHeight, txPage, blockPage) {
|
|
481
517
|
if (txPage !== undefined) {
|
|
482
518
|
cs.transactionStore.merge(Primitive_option.valFromOption(txPage));
|
|
@@ -485,8 +521,8 @@ function handleQueryResult(cs, query, newItems, newItemsWithDcs, latestFetchedBl
|
|
|
485
521
|
cs.blockStore.merge(Primitive_option.valFromOption(blockPage));
|
|
486
522
|
}
|
|
487
523
|
let fs = newItemsWithDcs.length !== 0 ? FetchState.registerDynamicContracts(cs.fetchState, cs.indexingAddresses, newItemsWithDcs) : cs.fetchState;
|
|
488
|
-
cs.fetchState = FetchState.updateKnownHeight(FetchState.handleQueryResult(fs,
|
|
489
|
-
cs.pendingBudget = Primitive_float.max(0, cs.pendingBudget - query.
|
|
524
|
+
cs.fetchState = FetchState.updateKnownHeight(FetchState.handleQueryResult(fs, query, latestFetchedBlock, newItems), knownHeight);
|
|
525
|
+
cs.pendingBudget = Primitive_float.max(0, cs.pendingBudget - query.itemsEst);
|
|
490
526
|
}
|
|
491
527
|
|
|
492
528
|
function registerReorgGuard(cs, blockHashes, knownHeight) {
|
|
@@ -550,7 +586,7 @@ function setEndBlockToFirstEvent(cs, blockNumber) {
|
|
|
550
586
|
}
|
|
551
587
|
|
|
552
588
|
function enterReorgThreshold(cs) {
|
|
553
|
-
cs.fetchState = FetchState.updateInternal(cs.fetchState, undefined, undefined, cs.chainConfig.blockLag, undefined);
|
|
589
|
+
cs.fetchState = FetchState.updateInternal(cs.fetchState, undefined, undefined, undefined, cs.chainConfig.blockLag, undefined);
|
|
554
590
|
}
|
|
555
591
|
|
|
556
592
|
function toChainMetadata(cs) {
|
|
@@ -598,7 +634,8 @@ function isReadyToEnterReorgThresholdAfterBatch(cs, batch) {
|
|
|
598
634
|
function advanceAfterBatch(cs, batch, enteringReorgThreshold) {
|
|
599
635
|
let chainAfterBatch = batch.progressedChainsById[cs.fetchState.chainId];
|
|
600
636
|
if (chainAfterBatch !== undefined) {
|
|
601
|
-
cs.fetchState = enteringReorgThreshold ? FetchState.updateInternal(chainAfterBatch.fetchState, undefined, undefined, cs.chainConfig.blockLag, undefined) : chainAfterBatch.fetchState;
|
|
637
|
+
cs.fetchState = enteringReorgThreshold ? FetchState.updateInternal(chainAfterBatch.fetchState, undefined, undefined, undefined, cs.chainConfig.blockLag, undefined) : chainAfterBatch.fetchState;
|
|
638
|
+
cs.processingBlockNumber = chainAfterBatch.progressBlockNumber;
|
|
602
639
|
return;
|
|
603
640
|
}
|
|
604
641
|
}
|
|
@@ -665,6 +702,7 @@ function applyBatchProgress(cs, batch, blockTimestampName) {
|
|
|
665
702
|
}
|
|
666
703
|
}
|
|
667
704
|
cs.committedProgressBlockNumber = chainAfterBatch.progressBlockNumber;
|
|
705
|
+
cs.processingBlockNumber = Primitive_int.max(cs.processingBlockNumber, chainAfterBatch.progressBlockNumber);
|
|
668
706
|
cs.numEventsProcessed = chainAfterBatch.totalEventsProcessed;
|
|
669
707
|
cs.transactionStore.prune(chainAfterBatch.progressBlockNumber);
|
|
670
708
|
cs.blockStore.prune(chainAfterBatch.progressBlockNumber);
|
|
@@ -692,6 +730,7 @@ function rollback(cs, newProgressBlockNumber, eventsProcessedDiff, rollbackTarge
|
|
|
692
730
|
cs.transactionStore.rollback(rollbackTargetBlockNumber);
|
|
693
731
|
cs.blockStore.rollback(rollbackTargetBlockNumber);
|
|
694
732
|
cs.committedProgressBlockNumber = Primitive_int.min(cs.committedProgressBlockNumber, rollbackTargetBlockNumber);
|
|
733
|
+
cs.processingBlockNumber = Primitive_int.min(cs.processingBlockNumber, rollbackTargetBlockNumber);
|
|
695
734
|
return;
|
|
696
735
|
} else {
|
|
697
736
|
return;
|
|
@@ -715,6 +754,7 @@ function rollback(cs, newProgressBlockNumber, eventsProcessedDiff, rollbackTarge
|
|
|
715
754
|
cs.transactionStore.rollback(newProgressBlockNumber);
|
|
716
755
|
cs.blockStore.rollback(newProgressBlockNumber);
|
|
717
756
|
cs.committedProgressBlockNumber = newProgressBlockNumber;
|
|
757
|
+
cs.processingBlockNumber = newProgressBlockNumber;
|
|
718
758
|
cs.numEventsProcessed = newTotalEventsProcessed;
|
|
719
759
|
}
|
|
720
760
|
|
|
@@ -740,11 +780,12 @@ export {
|
|
|
740
780
|
bufferReadyCount,
|
|
741
781
|
getProgressPercentage,
|
|
742
782
|
chainDensity,
|
|
783
|
+
effectiveDensity,
|
|
743
784
|
hasReadyItem,
|
|
744
785
|
isReadyToEnterReorgThreshold,
|
|
745
786
|
targetBlock,
|
|
746
|
-
progressAtBlock,
|
|
747
787
|
blockAtProgress,
|
|
788
|
+
frontierProgress,
|
|
748
789
|
getNextQuery,
|
|
749
790
|
dispatch,
|
|
750
791
|
toChainData,
|
|
@@ -757,6 +798,7 @@ export {
|
|
|
757
798
|
isReady,
|
|
758
799
|
isFetchingAtHead,
|
|
759
800
|
isAtHeadWithoutEndBlock,
|
|
801
|
+
filterByClientAddress,
|
|
760
802
|
handleQueryResult,
|
|
761
803
|
materializeBatchItems,
|
|
762
804
|
materializePageItems,
|
package/src/ChainState.resi
CHANGED
|
@@ -8,6 +8,7 @@ let configAddresses: Config.chain => array<Internal.indexingAddress>
|
|
|
8
8
|
let make: (
|
|
9
9
|
~chainConfig: Config.chain,
|
|
10
10
|
~fetchState: FetchState.t,
|
|
11
|
+
~onEventRegistrations: array<Internal.onEventRegistration>=?,
|
|
11
12
|
~indexingAddresses: IndexingAddresses.t,
|
|
12
13
|
~sourceManager: SourceManager.t,
|
|
13
14
|
~reorgDetection: ReorgDetection.t,
|
|
@@ -60,14 +61,21 @@ let bufferSize: t => int
|
|
|
60
61
|
let bufferReadyCount: t => int
|
|
61
62
|
let getProgressPercentage: t => float
|
|
62
63
|
let chainDensity: t => option<float>
|
|
64
|
+
let effectiveDensity: t => option<float>
|
|
63
65
|
let hasReadyItem: t => bool
|
|
64
66
|
let isReadyToEnterReorgThreshold: t => bool
|
|
65
67
|
|
|
66
68
|
// Fetch control.
|
|
67
69
|
let targetBlock: (t, ~chainTargetItems: float) => int
|
|
68
|
-
let progressAtBlock: (t, ~blockNumber: int) => float
|
|
69
70
|
let blockAtProgress: (t, ~progress: float) => int
|
|
70
|
-
let
|
|
71
|
+
let frontierProgress: t => float
|
|
72
|
+
let getNextQuery: (
|
|
73
|
+
t,
|
|
74
|
+
~chainTargetItems: float,
|
|
75
|
+
~chunkItemsMultiplier: float=?,
|
|
76
|
+
~itemsTargetFloor: int=?,
|
|
77
|
+
~maxTargetBlock: int=?,
|
|
78
|
+
) => FetchState.nextQuery
|
|
71
79
|
let dispatch: (
|
|
72
80
|
t,
|
|
73
81
|
~executeQuery: FetchState.query => promise<unit>,
|
|
@@ -106,6 +114,10 @@ let isReady: t => bool
|
|
|
106
114
|
let isFetchingAtHead: t => bool
|
|
107
115
|
let isAtHeadWithoutEndBlock: t => bool
|
|
108
116
|
|
|
117
|
+
// Drop over-fetched events an address-param filter rejects, before contract
|
|
118
|
+
// registration runs on them. Uses the chain's current indexing addresses.
|
|
119
|
+
let filterByClientAddress: (t, array<Internal.item>) => array<Internal.item>
|
|
120
|
+
|
|
109
121
|
// State transitions. The chain state is mutated only through these.
|
|
110
122
|
let handleQueryResult: (
|
|
111
123
|
t,
|
package/src/Config.res
CHANGED
|
@@ -200,14 +200,18 @@ let svmEventDescriptorSchema = S.schema(s =>
|
|
|
200
200
|
"transactionFields": s.matches(S.array(Internal.svmTransactionFieldSchema)),
|
|
201
201
|
"blockFields": s.matches(S.option(S.array(Internal.svmBlockFieldSchema))),
|
|
202
202
|
"includeLogs": s.matches(S.bool),
|
|
203
|
+
// An array of AND-groups OR-ed together: the CLI normalizes both the flat
|
|
204
|
+
// and `any_of` YAML shapes to `Vec<Vec<SvmAccountFilterJson>>`.
|
|
203
205
|
"accountFilters": s.matches(
|
|
204
206
|
S.option(
|
|
205
207
|
S.array(
|
|
206
|
-
S.
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
208
|
+
S.array(
|
|
209
|
+
S.schema(s =>
|
|
210
|
+
{
|
|
211
|
+
"position": s.matches(S.int),
|
|
212
|
+
"values": s.matches(S.array(S.string)),
|
|
213
|
+
}
|
|
214
|
+
),
|
|
211
215
|
),
|
|
212
216
|
),
|
|
213
217
|
),
|
package/src/Config.res.mjs
CHANGED
|
@@ -115,10 +115,10 @@ let svmEventDescriptorSchema = S$RescriptSchema.schema(s => ({
|
|
|
115
115
|
transactionFields: s.m(S$RescriptSchema.array(Internal.svmTransactionFieldSchema)),
|
|
116
116
|
blockFields: s.m(S$RescriptSchema.option(S$RescriptSchema.array(Internal.svmBlockFieldSchema))),
|
|
117
117
|
includeLogs: s.m(S$RescriptSchema.bool),
|
|
118
|
-
accountFilters: s.m(S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.schema(s => ({
|
|
118
|
+
accountFilters: s.m(S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.array(S$RescriptSchema.schema(s => ({
|
|
119
119
|
position: s.m(S$RescriptSchema.int),
|
|
120
120
|
values: s.m(S$RescriptSchema.array(S$RescriptSchema.string))
|
|
121
|
-
}))))),
|
|
121
|
+
})))))),
|
|
122
122
|
isInner: s.m(S$RescriptSchema.option(S$RescriptSchema.bool)),
|
|
123
123
|
accounts: s.m(S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.string))),
|
|
124
124
|
args: s.m(S$RescriptSchema.option(S$RescriptSchema.json(false)))
|