envio 3.0.0-alpha.2 → 3.0.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.
Files changed (60) hide show
  1. package/evm.schema.json +44 -33
  2. package/fuel.schema.json +32 -21
  3. package/index.d.ts +1 -0
  4. package/package.json +7 -6
  5. package/src/Batch.res.mjs +1 -1
  6. package/src/Benchmark.res +394 -0
  7. package/src/Benchmark.res.mjs +398 -0
  8. package/src/ChainFetcher.res +459 -0
  9. package/src/ChainFetcher.res.mjs +281 -0
  10. package/src/ChainManager.res +179 -0
  11. package/src/ChainManager.res.mjs +139 -0
  12. package/src/Config.res +15 -1
  13. package/src/Config.res.mjs +27 -4
  14. package/src/Ecosystem.res +9 -124
  15. package/src/Ecosystem.res.mjs +19 -160
  16. package/src/Env.res +0 -1
  17. package/src/Env.res.mjs +0 -3
  18. package/src/Envio.gen.ts +9 -1
  19. package/src/Envio.res +12 -9
  20. package/src/EventProcessing.res +476 -0
  21. package/src/EventProcessing.res.mjs +341 -0
  22. package/src/FetchState.res +54 -29
  23. package/src/FetchState.res.mjs +62 -35
  24. package/src/GlobalState.res +1169 -0
  25. package/src/GlobalState.res.mjs +1196 -0
  26. package/src/Internal.res +2 -1
  27. package/src/LoadLayer.res +444 -0
  28. package/src/LoadLayer.res.mjs +296 -0
  29. package/src/LoadLayer.resi +32 -0
  30. package/src/Prometheus.res +8 -8
  31. package/src/Prometheus.res.mjs +10 -10
  32. package/src/ReorgDetection.res +6 -10
  33. package/src/ReorgDetection.res.mjs +6 -6
  34. package/src/UserContext.res +356 -0
  35. package/src/UserContext.res.mjs +238 -0
  36. package/src/bindings/DateFns.res +71 -0
  37. package/src/bindings/DateFns.res.mjs +22 -0
  38. package/src/sources/Evm.res +87 -0
  39. package/src/sources/Evm.res.mjs +105 -0
  40. package/src/sources/EvmChain.res +95 -0
  41. package/src/sources/EvmChain.res.mjs +61 -0
  42. package/src/sources/Fuel.res +19 -34
  43. package/src/sources/Fuel.res.mjs +34 -16
  44. package/src/sources/FuelSDK.res +37 -0
  45. package/src/sources/FuelSDK.res.mjs +29 -0
  46. package/src/sources/HyperFuel.res +2 -2
  47. package/src/sources/HyperFuel.resi +1 -1
  48. package/src/sources/HyperFuelClient.res +2 -2
  49. package/src/sources/HyperFuelSource.res +8 -8
  50. package/src/sources/HyperFuelSource.res.mjs +5 -5
  51. package/src/sources/HyperSyncSource.res +5 -5
  52. package/src/sources/HyperSyncSource.res.mjs +5 -5
  53. package/src/sources/RpcSource.res +4 -4
  54. package/src/sources/RpcSource.res.mjs +3 -3
  55. package/src/sources/Solana.res +59 -0
  56. package/src/sources/Solana.res.mjs +79 -0
  57. package/src/sources/Source.res +2 -2
  58. package/src/sources/SourceManager.res +24 -32
  59. package/src/sources/SourceManager.res.mjs +20 -20
  60. package/src/sources/SourceManager.resi +4 -5
@@ -0,0 +1,281 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Env from "./Env.res.mjs";
4
+ import * as Caml from "rescript/lib/es6/caml.js";
5
+ import * as Utils from "./Utils.res.mjs";
6
+ import * as Js_exn from "rescript/lib/es6/js_exn.js";
7
+ import * as Logging from "./Logging.res.mjs";
8
+ import * as $$Promise from "./bindings/Promise.res.mjs";
9
+ import * as ChainMap from "./ChainMap.res.mjs";
10
+ import * as Belt_Array from "rescript/lib/es6/belt_Array.js";
11
+ import * as FetchState from "./FetchState.res.mjs";
12
+ import * as Prometheus from "./Prometheus.res.mjs";
13
+ import * as Belt_Option from "rescript/lib/es6/belt_Option.js";
14
+ import * as Belt_Result from "rescript/lib/es6/belt_Result.js";
15
+ import * as Caml_option from "rescript/lib/es6/caml_option.js";
16
+ import * as EventRouter from "./sources/EventRouter.res.mjs";
17
+ import * as UserContext from "./UserContext.res.mjs";
18
+ import * as ErrorHandling from "./ErrorHandling.res.mjs";
19
+ import * as SourceManager from "./sources/SourceManager.res.mjs";
20
+ import * as ReorgDetection from "./ReorgDetection.res.mjs";
21
+ import * as Caml_js_exceptions from "rescript/lib/es6/caml_js_exceptions.js";
22
+ import * as SafeCheckpointTracking from "./SafeCheckpointTracking.res.mjs";
23
+
24
+ function make(chainConfig, dynamicContracts, startBlock, endBlock, firstEventBlockNumber, progressBlockNumber, config, registrations, targetBufferSize, logger, timestampCaughtUpToHeadOrEndblock, numEventsProcessed, numBatchesFetched, isInReorgThreshold, reorgCheckpoints, maxReorgDepth) {
25
+ var eventRouter = EventRouter.empty();
26
+ var contracts = [];
27
+ var eventConfigs = [];
28
+ var notRegisteredEvents = [];
29
+ Belt_Array.forEach(chainConfig.contracts, (function (contract) {
30
+ var contractName = contract.name;
31
+ Belt_Array.forEach(contract.events, (function (eventConfig) {
32
+ var hasContractRegister = Belt_Option.isSome(eventConfig.contractRegister);
33
+ EventRouter.addOrThrow(eventRouter, eventConfig.id, undefined, contractName, eventConfig.isWildcard, eventConfig.name, ChainMap.Chain.makeUnsafe(chainConfig.id));
34
+ var shouldBeIncluded;
35
+ if (config.enableRawEvents) {
36
+ shouldBeIncluded = true;
37
+ } else {
38
+ var isRegistered = hasContractRegister || Belt_Option.isSome(eventConfig.handler);
39
+ if (!isRegistered) {
40
+ notRegisteredEvents.push(eventConfig);
41
+ }
42
+ shouldBeIncluded = isRegistered;
43
+ }
44
+ if (shouldBeIncluded) {
45
+ eventConfigs.push(eventConfig);
46
+ return ;
47
+ }
48
+
49
+ }));
50
+ var startBlock = contract.startBlock;
51
+ if (startBlock !== undefined && startBlock < chainConfig.startBlock) {
52
+ Js_exn.raiseError("The start block for contract \"" + contractName + "\" is less than the chain start block. This is not supported yet.");
53
+ }
54
+ Belt_Array.forEach(contract.addresses, (function (address) {
55
+ var startBlock = contract.startBlock;
56
+ contracts.push({
57
+ address: address,
58
+ contractName: contract.name,
59
+ startBlock: startBlock !== undefined ? startBlock : chainConfig.startBlock,
60
+ registrationBlock: undefined
61
+ });
62
+ }));
63
+ }));
64
+ Belt_Array.forEach(dynamicContracts, (function (dc) {
65
+ contracts.push(dc);
66
+ }));
67
+ if (Utils.$$Array.notEmpty(notRegisteredEvents)) {
68
+ Logging.childInfo(logger, "The event" + (
69
+ notRegisteredEvents.length > 1 ? "s" : ""
70
+ ) + " " + Belt_Array.map(notRegisteredEvents, (function (eventConfig) {
71
+ return eventConfig.contractName + "." + eventConfig.name;
72
+ })).join(", ") + " don't have an event handler and skipped for indexing.");
73
+ }
74
+ var onBlockConfigs = registrations.onBlockByChainId[String(chainConfig.id)];
75
+ if (onBlockConfigs !== undefined) {
76
+ Belt_Array.forEach(onBlockConfigs, (function (onBlockConfig) {
77
+ if (Belt_Option.getWithDefault(onBlockConfig.startBlock, startBlock) < startBlock) {
78
+ Js_exn.raiseError("The start block for onBlock handler \"" + onBlockConfig.name + "\" is less than the chain start block (" + String(startBlock) + "). This is not supported yet.");
79
+ }
80
+ if (endBlock !== undefined && Belt_Option.getWithDefault(onBlockConfig.endBlock, endBlock) > endBlock) {
81
+ return Js_exn.raiseError("The end block for onBlock handler \"" + onBlockConfig.name + "\" is greater than the chain end block (" + String(endBlock) + "). This is not supported yet.");
82
+ }
83
+
84
+ }));
85
+ }
86
+ var fetchState = FetchState.make(startBlock, endBlock, eventConfigs, contracts, config.maxAddrInPartition, chainConfig.id, targetBufferSize, 0, progressBlockNumber, onBlockConfigs, Caml.int_max(!config.shouldRollbackOnReorg || isInReorgThreshold ? 0 : chainConfig.maxReorgDepth, Belt_Option.getWithDefault(Env.indexingBlockLag, 0)));
87
+ var chainReorgCheckpoints = Belt_Array.keepMapU(reorgCheckpoints, (function (reorgCheckpoint) {
88
+ if (reorgCheckpoint.chain_id === chainConfig.id) {
89
+ return reorgCheckpoint;
90
+ }
91
+
92
+ }));
93
+ return {
94
+ logger: logger,
95
+ fetchState: fetchState,
96
+ sourceManager: SourceManager.make(chainConfig.sources, Env.maxPartitionConcurrency, undefined, undefined, undefined),
97
+ chainConfig: chainConfig,
98
+ isProgressAtHead: false,
99
+ timestampCaughtUpToHeadOrEndblock: timestampCaughtUpToHeadOrEndblock,
100
+ committedProgressBlockNumber: progressBlockNumber,
101
+ firstEventBlockNumber: firstEventBlockNumber,
102
+ numEventsProcessed: numEventsProcessed,
103
+ numBatchesFetched: numBatchesFetched,
104
+ reorgDetection: ReorgDetection.make(chainReorgCheckpoints, maxReorgDepth, config.shouldRollbackOnReorg),
105
+ safeCheckpointTracking: SafeCheckpointTracking.make(maxReorgDepth, config.shouldRollbackOnReorg, chainReorgCheckpoints)
106
+ };
107
+ }
108
+
109
+ function makeFromConfig(chainConfig, config, registrations, targetBufferSize) {
110
+ var logger = Logging.createChild({
111
+ chainId: chainConfig.id
112
+ });
113
+ return make(chainConfig, [], chainConfig.startBlock, chainConfig.endBlock, undefined, -1, config, registrations, targetBufferSize, logger, undefined, 0, 0, false, [], chainConfig.maxReorgDepth);
114
+ }
115
+
116
+ async function makeFromDbState(chainConfig, resumedChainState, reorgCheckpoints, isInReorgThreshold, config, registrations, targetBufferSize) {
117
+ var chainId = chainConfig.id;
118
+ var logger = Logging.createChild({
119
+ chainId: chainId
120
+ });
121
+ Prometheus.ProgressEventsCount.set(resumedChainState.numEventsProcessed, chainId);
122
+ var progressBlockNumber = resumedChainState.progressBlockNumber >= 0 ? resumedChainState.progressBlockNumber : resumedChainState.startBlock - 1 | 0;
123
+ return make(chainConfig, resumedChainState.dynamicContracts, resumedChainState.startBlock, resumedChainState.endBlock, resumedChainState.firstEventBlockNumber, progressBlockNumber, config, registrations, targetBufferSize, logger, Env.updateSyncTimeOnRestart ? undefined : resumedChainState.timestampCaughtUpToHeadOrEndblock, resumedChainState.numEventsProcessed, 0, isInReorgThreshold, reorgCheckpoints, resumedChainState.maxReorgDepth);
124
+ }
125
+
126
+ function getContractStartBlock(config, chain, contractName) {
127
+ var chainConfig = ChainMap.get(config.chainMap, chain);
128
+ return Belt_Option.flatMap(Caml_option.undefined_to_opt(chainConfig.contracts.find(function (contract) {
129
+ return contract.name === contractName;
130
+ })), (function (contract) {
131
+ return contract.startBlock;
132
+ }));
133
+ }
134
+
135
+ async function runContractRegistersOrThrow(itemsWithContractRegister, chain, config) {
136
+ var itemsWithDcs = [];
137
+ var onRegister = function (item, contractAddress, contractName) {
138
+ var blockNumber = item.blockNumber;
139
+ var configuredStartBlock = getContractStartBlock(config, chain, contractName);
140
+ var contractStartBlock = configuredStartBlock !== undefined ? configuredStartBlock : blockNumber;
141
+ var dc_registrationBlock = blockNumber;
142
+ var dc = {
143
+ address: contractAddress,
144
+ contractName: contractName,
145
+ startBlock: contractStartBlock,
146
+ registrationBlock: dc_registrationBlock
147
+ };
148
+ var dcs = item.dcs;
149
+ if (dcs !== undefined) {
150
+ dcs.push(dc);
151
+ } else {
152
+ item.dcs = [dc];
153
+ itemsWithDcs.push(item);
154
+ }
155
+ };
156
+ var promises = [];
157
+ for(var idx = 0 ,idx_finish = itemsWithContractRegister.length; idx < idx_finish; ++idx){
158
+ var item = itemsWithContractRegister[idx];
159
+ var match = item.eventConfig;
160
+ var contractRegister = match.contractRegister;
161
+ var contractRegister$1 = contractRegister !== undefined ? contractRegister : Js_exn.raiseError("Contract register is not set for event " + match.name);
162
+ var errorMessage = "Event contractRegister failed, please fix the error to keep the indexer running smoothly";
163
+ try {
164
+ var params = {
165
+ item: item,
166
+ onRegister: onRegister,
167
+ config: config,
168
+ isResolved: false
169
+ };
170
+ var result = contractRegister$1(UserContext.getContractRegisterArgs(params));
171
+ if ($$Promise.isCatchable(result)) {
172
+ promises.push($$Promise.$$catch(result.then((function(params){
173
+ return function (r) {
174
+ params.isResolved = true;
175
+
176
+ }
177
+ }(params))), (function(item,params){
178
+ return function (exn) {
179
+ params.isResolved = true;
180
+ return ErrorHandling.mkLogAndRaise(Logging.getItemLogger(item), errorMessage, exn);
181
+ }
182
+ }(item,params))));
183
+ } else {
184
+ params.isResolved = true;
185
+ }
186
+ }
187
+ catch (raw_exn){
188
+ var exn = Caml_js_exceptions.internalToOCamlException(raw_exn);
189
+ ErrorHandling.mkLogAndRaise(Logging.getItemLogger(item), errorMessage, exn);
190
+ }
191
+ }
192
+ if (Utils.$$Array.notEmpty(promises)) {
193
+ await Promise.all(promises);
194
+ }
195
+ return itemsWithDcs;
196
+ }
197
+
198
+ function handleQueryResult(chainFetcher, query, newItems, newItemsWithDcs, latestFetchedBlock, knownHeight) {
199
+ var fs = newItemsWithDcs.length !== 0 ? FetchState.registerDynamicContracts(chainFetcher.fetchState, newItemsWithDcs) : chainFetcher.fetchState;
200
+ return Belt_Result.map(FetchState.handleQueryResult(fs, query, latestFetchedBlock, newItems), (function (fs) {
201
+ return {
202
+ logger: chainFetcher.logger,
203
+ fetchState: FetchState.updateKnownHeight(fs, knownHeight),
204
+ sourceManager: chainFetcher.sourceManager,
205
+ chainConfig: chainFetcher.chainConfig,
206
+ isProgressAtHead: chainFetcher.isProgressAtHead,
207
+ timestampCaughtUpToHeadOrEndblock: chainFetcher.timestampCaughtUpToHeadOrEndblock,
208
+ committedProgressBlockNumber: chainFetcher.committedProgressBlockNumber,
209
+ firstEventBlockNumber: chainFetcher.firstEventBlockNumber,
210
+ numEventsProcessed: chainFetcher.numEventsProcessed,
211
+ numBatchesFetched: chainFetcher.numBatchesFetched,
212
+ reorgDetection: chainFetcher.reorgDetection,
213
+ safeCheckpointTracking: chainFetcher.safeCheckpointTracking
214
+ };
215
+ }));
216
+ }
217
+
218
+ function hasProcessedToEndblock(self) {
219
+ var endBlock = self.fetchState.endBlock;
220
+ if (endBlock !== undefined) {
221
+ return self.committedProgressBlockNumber >= endBlock;
222
+ } else {
223
+ return false;
224
+ }
225
+ }
226
+
227
+ function hasNoMoreEventsToProcess(self) {
228
+ return FetchState.bufferSize(self.fetchState) === 0;
229
+ }
230
+
231
+ function getHighestBlockBelowThreshold(cf) {
232
+ var highestBlockBelowThreshold = cf.fetchState.knownHeight - cf.chainConfig.maxReorgDepth | 0;
233
+ if (highestBlockBelowThreshold < 0) {
234
+ return 0;
235
+ } else {
236
+ return highestBlockBelowThreshold;
237
+ }
238
+ }
239
+
240
+ async function getLastKnownValidBlock(chainFetcher, reorgBlockNumber, getBlockHashesOpt) {
241
+ var getBlockHashes = getBlockHashesOpt !== undefined ? getBlockHashesOpt : SourceManager.getActiveSource(chainFetcher.sourceManager).getBlockHashes;
242
+ var scannedBlockNumbers = ReorgDetection.getThresholdBlockNumbersBelowBlock(chainFetcher.reorgDetection, reorgBlockNumber, chainFetcher.fetchState.knownHeight);
243
+ var getBlockHashes$1 = function (blockNumbers) {
244
+ return getBlockHashes(blockNumbers, chainFetcher.logger).then(function (res) {
245
+ if (res.TAG === "Ok") {
246
+ return res._0;
247
+ } else {
248
+ return ErrorHandling.mkLogAndRaise(undefined, "Failed to fetch blockHashes for given blockNumbers during rollback", res._0);
249
+ }
250
+ });
251
+ };
252
+ if (scannedBlockNumbers.length === 0) {
253
+ return getHighestBlockBelowThreshold(chainFetcher);
254
+ }
255
+ var blockNumbersAndHashes = await getBlockHashes$1(scannedBlockNumbers);
256
+ var blockNumber = ReorgDetection.getLatestValidScannedBlock(chainFetcher.reorgDetection, blockNumbersAndHashes);
257
+ if (blockNumber !== undefined) {
258
+ return blockNumber;
259
+ } else {
260
+ return getHighestBlockBelowThreshold(chainFetcher);
261
+ }
262
+ }
263
+
264
+ function isActivelyIndexing(chainFetcher) {
265
+ return FetchState.isActivelyIndexing(chainFetcher.fetchState);
266
+ }
267
+
268
+ export {
269
+ make ,
270
+ makeFromConfig ,
271
+ makeFromDbState ,
272
+ getContractStartBlock ,
273
+ runContractRegistersOrThrow ,
274
+ handleQueryResult ,
275
+ hasProcessedToEndblock ,
276
+ hasNoMoreEventsToProcess ,
277
+ getHighestBlockBelowThreshold ,
278
+ getLastKnownValidBlock ,
279
+ isActivelyIndexing ,
280
+ }
281
+ /* Env Not a pure module */
@@ -0,0 +1,179 @@
1
+ open Belt
2
+
3
+ type t = {
4
+ committedCheckpointId: float,
5
+ chainFetchers: ChainMap.t<ChainFetcher.t>,
6
+ multichain: Config.multichain,
7
+ isInReorgThreshold: bool,
8
+ }
9
+
10
+ let calculateTargetBufferSize = (~activeChainsCount, ~config: Config.t) => {
11
+ let targetBatchesInBuffer = 3
12
+ switch Env.targetBufferSize {
13
+ | Some(size) => size
14
+ | None =>
15
+ config.batchSize * (activeChainsCount > targetBatchesInBuffer ? 1 : targetBatchesInBuffer)
16
+ }
17
+ }
18
+
19
+ let makeFromConfig = (~config: Config.t, ~registrations): t => {
20
+ let targetBufferSize = calculateTargetBufferSize(
21
+ ~activeChainsCount=config.chainMap->ChainMap.size,
22
+ ~config,
23
+ )
24
+ let chainFetchers =
25
+ config.chainMap->ChainMap.map(
26
+ ChainFetcher.makeFromConfig(_, ~config, ~registrations, ~targetBufferSize),
27
+ )
28
+ {
29
+ committedCheckpointId: 0.,
30
+ chainFetchers,
31
+ multichain: config.multichain,
32
+ isInReorgThreshold: false,
33
+ }
34
+ }
35
+
36
+ let makeFromDbState = async (
37
+ ~initialState: Persistence.initialState,
38
+ ~config: Config.t,
39
+ ~registrations,
40
+ ~persistence: Persistence.t,
41
+ ): t => {
42
+ let isInReorgThreshold = if initialState.cleanRun {
43
+ false
44
+ } else {
45
+ // TODO: Move to Persistence.initialState
46
+ // Since now it's possible not to have rows in the history table
47
+ // even after the indexer started saving history (entered reorg threshold),
48
+ // This rows check might incorrectly return false for recovering the isInReorgThreshold option.
49
+ // But this is not a problem. There's no history anyways, and the indexer will be able to
50
+ // correctly calculate isInReorgThreshold as it starts.
51
+ let hasStartedSavingHistory = await persistence.storage.hasEntityHistoryRows()
52
+
53
+ //If we have started saving history, continue to save history
54
+ //as regardless of whether we are still in a reorg threshold
55
+ hasStartedSavingHistory
56
+ }
57
+
58
+ let targetBufferSize = calculateTargetBufferSize(
59
+ ~activeChainsCount=initialState.chains->Array.length,
60
+ ~config,
61
+ )
62
+ Prometheus.ProcessingMaxBatchSize.set(~maxBatchSize=config.batchSize)
63
+ Prometheus.IndexingTargetBufferSize.set(~targetBufferSize)
64
+ Prometheus.ReorgThreshold.set(~isInReorgThreshold)
65
+ initialState.cache->Utils.Dict.forEach(({effectName, count}) => {
66
+ Prometheus.EffectCacheCount.set(~count, ~effectName)
67
+ })
68
+
69
+ let chainFetchersArr =
70
+ await initialState.chains
71
+ ->Array.map(async (resumedChainState: Persistence.initialChainState) => {
72
+ let chain = Config.getChain(config, ~chainId=resumedChainState.id)
73
+ let chainConfig = config.chainMap->ChainMap.get(chain)
74
+
75
+ (
76
+ chain,
77
+ await chainConfig->ChainFetcher.makeFromDbState(
78
+ ~resumedChainState,
79
+ ~reorgCheckpoints=initialState.reorgCheckpoints,
80
+ ~isInReorgThreshold,
81
+ ~targetBufferSize,
82
+ ~config,
83
+ ~registrations,
84
+ ),
85
+ )
86
+ })
87
+ ->Promise.all
88
+
89
+ let chainFetchers = ChainMap.fromArrayUnsafe(chainFetchersArr)
90
+
91
+ {
92
+ committedCheckpointId: initialState.checkpointId,
93
+ multichain: config.multichain,
94
+ chainFetchers,
95
+ isInReorgThreshold,
96
+ }
97
+ }
98
+
99
+ let getChainFetcher = (chainManager: t, ~chain: ChainMap.Chain.t): ChainFetcher.t => {
100
+ chainManager.chainFetchers->ChainMap.get(chain)
101
+ }
102
+
103
+ let setChainFetcher = (chainManager: t, chainFetcher: ChainFetcher.t) => {
104
+ {
105
+ ...chainManager,
106
+ chainFetchers: chainManager.chainFetchers->ChainMap.set(
107
+ ChainMap.Chain.makeUnsafe(~chainId=chainFetcher.chainConfig.id),
108
+ chainFetcher,
109
+ ),
110
+ }
111
+ }
112
+
113
+ let nextItemIsNone = (chainManager: t): bool => {
114
+ !Batch.hasMultichainReadyItem(
115
+ chainManager.chainFetchers->ChainMap.map(cf => {
116
+ cf.fetchState
117
+ }),
118
+ ~multichain=chainManager.multichain,
119
+ )
120
+ }
121
+
122
+ let createBatch = (chainManager: t, ~batchSizeTarget: int, ~isRollback: bool): Batch.t => {
123
+ Batch.make(
124
+ ~checkpointIdBeforeBatch=chainManager.committedCheckpointId +. (
125
+ // Since for rollback we have a diff checkpoint id.
126
+ // This is needed to currectly overwrite old state
127
+ // in an append-only ClickHouse insert.
128
+ isRollback ? 1. : 0.
129
+ ),
130
+ ~chainsBeforeBatch=chainManager.chainFetchers->ChainMap.map((cf): Batch.chainBeforeBatch => {
131
+ fetchState: cf.fetchState,
132
+ progressBlockNumber: cf.committedProgressBlockNumber,
133
+ totalEventsProcessed: cf.numEventsProcessed,
134
+ sourceBlockNumber: cf.fetchState.knownHeight,
135
+ reorgDetection: cf.reorgDetection,
136
+ }),
137
+ ~multichain=chainManager.multichain,
138
+ ~batchSizeTarget,
139
+ )
140
+ }
141
+
142
+ let isProgressAtHead = chainManager =>
143
+ chainManager.chainFetchers
144
+ ->ChainMap.values
145
+ ->Js.Array2.every(cf => cf.isProgressAtHead)
146
+
147
+ let isActivelyIndexing = chainManager =>
148
+ chainManager.chainFetchers
149
+ ->ChainMap.values
150
+ ->Js.Array2.every(ChainFetcher.isActivelyIndexing)
151
+
152
+ let getSafeCheckpointId = (chainManager: t) => {
153
+ let chainFetchers = chainManager.chainFetchers->ChainMap.values
154
+
155
+ let infinity = (%raw(`Infinity`): float)
156
+ let result = ref(infinity)
157
+
158
+ for idx in 0 to chainFetchers->Array.length - 1 {
159
+ let chainFetcher = chainFetchers->Array.getUnsafe(idx)
160
+ switch chainFetcher.safeCheckpointTracking {
161
+ | None => () // Skip chains with maxReorgDepth = 0
162
+ | Some(safeCheckpointTracking) => {
163
+ let safeCheckpointId =
164
+ safeCheckpointTracking->SafeCheckpointTracking.getSafeCheckpointId(
165
+ ~sourceBlockNumber=chainFetcher.fetchState.knownHeight,
166
+ )
167
+ if safeCheckpointId < result.contents {
168
+ result := safeCheckpointId
169
+ }
170
+ }
171
+ }
172
+ }
173
+
174
+ if result.contents === infinity || result.contents === 0. {
175
+ None // No safe checkpoint found
176
+ } else {
177
+ Some(result.contents)
178
+ }
179
+ }
@@ -0,0 +1,139 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Env from "./Env.res.mjs";
4
+ import * as Batch from "./Batch.res.mjs";
5
+ import * as Utils from "./Utils.res.mjs";
6
+ import * as Config from "./Config.res.mjs";
7
+ import * as ChainMap from "./ChainMap.res.mjs";
8
+ import * as Belt_Array from "rescript/lib/es6/belt_Array.js";
9
+ import * as Prometheus from "./Prometheus.res.mjs";
10
+ import * as ChainFetcher from "./ChainFetcher.res.mjs";
11
+ import * as SafeCheckpointTracking from "./SafeCheckpointTracking.res.mjs";
12
+
13
+ function calculateTargetBufferSize(activeChainsCount, config) {
14
+ if (Env.targetBufferSize !== undefined) {
15
+ return Env.targetBufferSize;
16
+ } else {
17
+ return Math.imul(config.batchSize, activeChainsCount > 3 ? 1 : 3);
18
+ }
19
+ }
20
+
21
+ function makeFromConfig(config, registrations) {
22
+ var targetBufferSize = calculateTargetBufferSize(ChainMap.size(config.chainMap), config);
23
+ var chainFetchers = ChainMap.map(config.chainMap, (function (__x) {
24
+ return ChainFetcher.makeFromConfig(__x, config, registrations, targetBufferSize);
25
+ }));
26
+ return {
27
+ committedCheckpointId: 0,
28
+ chainFetchers: chainFetchers,
29
+ multichain: config.multichain,
30
+ isInReorgThreshold: false
31
+ };
32
+ }
33
+
34
+ async function makeFromDbState(initialState, config, registrations, persistence) {
35
+ var isInReorgThreshold = initialState.cleanRun ? false : await persistence.storage.hasEntityHistoryRows();
36
+ var targetBufferSize = calculateTargetBufferSize(initialState.chains.length, config);
37
+ Prometheus.ProcessingMaxBatchSize.set(config.batchSize);
38
+ Prometheus.IndexingTargetBufferSize.set(targetBufferSize);
39
+ Prometheus.ReorgThreshold.set(isInReorgThreshold);
40
+ Utils.Dict.forEach(initialState.cache, (function (param) {
41
+ var count = param.count;
42
+ Prometheus.EffectCacheCount.set(count, param.effectName);
43
+ }));
44
+ var chainFetchersArr = await Promise.all(Belt_Array.map(initialState.chains, (async function (resumedChainState) {
45
+ var chain = Config.getChain(config, resumedChainState.id);
46
+ var chainConfig = ChainMap.get(config.chainMap, chain);
47
+ return [
48
+ chain,
49
+ await ChainFetcher.makeFromDbState(chainConfig, resumedChainState, initialState.reorgCheckpoints, isInReorgThreshold, config, registrations, targetBufferSize)
50
+ ];
51
+ })));
52
+ var chainFetchers = ChainMap.fromArrayUnsafe(chainFetchersArr);
53
+ return {
54
+ committedCheckpointId: initialState.checkpointId,
55
+ chainFetchers: chainFetchers,
56
+ multichain: config.multichain,
57
+ isInReorgThreshold: isInReorgThreshold
58
+ };
59
+ }
60
+
61
+ function getChainFetcher(chainManager, chain) {
62
+ return ChainMap.get(chainManager.chainFetchers, chain);
63
+ }
64
+
65
+ function setChainFetcher(chainManager, chainFetcher) {
66
+ return {
67
+ committedCheckpointId: chainManager.committedCheckpointId,
68
+ chainFetchers: ChainMap.set(chainManager.chainFetchers, ChainMap.Chain.makeUnsafe(chainFetcher.chainConfig.id), chainFetcher),
69
+ multichain: chainManager.multichain,
70
+ isInReorgThreshold: chainManager.isInReorgThreshold
71
+ };
72
+ }
73
+
74
+ function nextItemIsNone(chainManager) {
75
+ return !Batch.hasMultichainReadyItem(ChainMap.map(chainManager.chainFetchers, (function (cf) {
76
+ return cf.fetchState;
77
+ })), chainManager.multichain);
78
+ }
79
+
80
+ function createBatch(chainManager, batchSizeTarget, isRollback) {
81
+ return Batch.make(chainManager.committedCheckpointId + (
82
+ isRollback ? 1 : 0
83
+ ), ChainMap.map(chainManager.chainFetchers, (function (cf) {
84
+ return {
85
+ fetchState: cf.fetchState,
86
+ reorgDetection: cf.reorgDetection,
87
+ progressBlockNumber: cf.committedProgressBlockNumber,
88
+ sourceBlockNumber: cf.fetchState.knownHeight,
89
+ totalEventsProcessed: cf.numEventsProcessed
90
+ };
91
+ })), chainManager.multichain, batchSizeTarget);
92
+ }
93
+
94
+ function isProgressAtHead(chainManager) {
95
+ return ChainMap.values(chainManager.chainFetchers).every(function (cf) {
96
+ return cf.isProgressAtHead;
97
+ });
98
+ }
99
+
100
+ function isActivelyIndexing(chainManager) {
101
+ return ChainMap.values(chainManager.chainFetchers).every(ChainFetcher.isActivelyIndexing);
102
+ }
103
+
104
+ function getSafeCheckpointId(chainManager) {
105
+ var chainFetchers = ChainMap.values(chainManager.chainFetchers);
106
+ var infinity = Infinity;
107
+ var result = infinity;
108
+ for(var idx = 0 ,idx_finish = chainFetchers.length; idx < idx_finish; ++idx){
109
+ var chainFetcher = chainFetchers[idx];
110
+ var safeCheckpointTracking = chainFetcher.safeCheckpointTracking;
111
+ if (safeCheckpointTracking !== undefined) {
112
+ var safeCheckpointId = SafeCheckpointTracking.getSafeCheckpointId(safeCheckpointTracking, chainFetcher.fetchState.knownHeight);
113
+ if (safeCheckpointId < result) {
114
+ result = safeCheckpointId;
115
+ }
116
+
117
+ }
118
+
119
+ }
120
+ if (result === infinity || result === 0) {
121
+ return ;
122
+ } else {
123
+ return result;
124
+ }
125
+ }
126
+
127
+ export {
128
+ calculateTargetBufferSize ,
129
+ makeFromConfig ,
130
+ makeFromDbState ,
131
+ getChainFetcher ,
132
+ setChainFetcher ,
133
+ nextItemIsNone ,
134
+ createBatch ,
135
+ isProgressAtHead ,
136
+ isActivelyIndexing ,
137
+ getSafeCheckpointId ,
138
+ }
139
+ /* Env Not a pure module */
package/src/Config.res CHANGED
@@ -51,6 +51,7 @@ type t = {
51
51
  batchSize: int,
52
52
  lowercaseAddresses: bool,
53
53
  addContractNameToContractNameMapping: dict<string>,
54
+ userEntitiesByName: dict<Internal.entityConfig>,
54
55
  }
55
56
 
56
57
  let make = (
@@ -64,6 +65,7 @@ let make = (
64
65
  ~multichain=Unordered,
65
66
  ~shouldUseHypersyncClientDecoder=true,
66
67
  ~maxAddrInPartition=5000,
68
+ ~userEntities: array<Internal.entityConfig>=[],
67
69
  ) => {
68
70
  // Validate that lowercase addresses is not used with viem decoder
69
71
  if lowercaseAddresses && !shouldUseHypersyncClientDecoder {
@@ -88,7 +90,18 @@ let make = (
88
90
  })
89
91
  })
90
92
 
91
- let ecosystem = Ecosystem.fromName(ecosystem)
93
+ let ecosystem = switch ecosystem {
94
+ | Ecosystem.Evm => Evm.ecosystem
95
+ | Ecosystem.Fuel => Fuel.ecosystem
96
+ | Ecosystem.Solana => Solana.ecosystem
97
+ }
98
+
99
+ let userEntitiesByName =
100
+ userEntities
101
+ ->Js.Array2.map(entityConfig => {
102
+ (entityConfig.name, entityConfig)
103
+ })
104
+ ->Js.Dict.fromArray
92
105
 
93
106
  {
94
107
  shouldRollbackOnReorg,
@@ -102,6 +115,7 @@ let make = (
102
115
  batchSize,
103
116
  lowercaseAddresses,
104
117
  addContractNameToContractNameMapping,
118
+ userEntitiesByName,
105
119
  }
106
120
  }
107
121