envio 3.3.0-alpha.8 → 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.
Files changed (107) hide show
  1. package/index.d.ts +19 -0
  2. package/licenses/CLA.md +35 -0
  3. package/licenses/EULA.md +67 -0
  4. package/licenses/LICENSE.md +45 -0
  5. package/licenses/README.md +35 -0
  6. package/package.json +9 -8
  7. package/src/Batch.res.mjs +1 -1
  8. package/src/BatchProcessing.res +0 -7
  9. package/src/BatchProcessing.res.mjs +1 -8
  10. package/src/ChainFetching.res +30 -18
  11. package/src/ChainFetching.res.mjs +23 -13
  12. package/src/ChainState.res +205 -59
  13. package/src/ChainState.res.mjs +130 -27
  14. package/src/ChainState.resi +16 -1
  15. package/src/Config.res +9 -5
  16. package/src/Config.res.mjs +2 -2
  17. package/src/Core.res +20 -0
  18. package/src/Core.res.mjs +12 -0
  19. package/src/CrossChainState.res +147 -39
  20. package/src/CrossChainState.res.mjs +42 -10
  21. package/src/EffectState.res +100 -0
  22. package/src/EffectState.res.mjs +74 -0
  23. package/src/EffectState.resi +32 -0
  24. package/src/Envio.res +25 -7
  25. package/src/Envio.res.mjs +15 -19
  26. package/src/EnvioGlobal.res +53 -0
  27. package/src/EnvioGlobal.res.mjs +31 -0
  28. package/src/EventConfigBuilder.res +30 -56
  29. package/src/EventConfigBuilder.res.mjs +24 -30
  30. package/src/EventProcessing.res +8 -5
  31. package/src/EventProcessing.res.mjs +2 -1
  32. package/src/FetchState.res +842 -474
  33. package/src/FetchState.res.mjs +545 -356
  34. package/src/HandlerLoader.res +1 -1
  35. package/src/HandlerLoader.res.mjs +1 -1
  36. package/src/HandlerRegister.res +478 -305
  37. package/src/HandlerRegister.res.mjs +277 -209
  38. package/src/HandlerRegister.resi +11 -6
  39. package/src/InMemoryStore.res +14 -26
  40. package/src/InMemoryStore.res.mjs +6 -19
  41. package/src/IndexerState.res +15 -39
  42. package/src/IndexerState.res.mjs +18 -29
  43. package/src/IndexerState.resi +2 -10
  44. package/src/Internal.res +121 -18
  45. package/src/Internal.res.mjs +96 -2
  46. package/src/LoadLayer.res +44 -24
  47. package/src/LoadLayer.res.mjs +43 -20
  48. package/src/LoadLayer.resi +1 -0
  49. package/src/LogSelection.res +92 -217
  50. package/src/LogSelection.res.mjs +95 -184
  51. package/src/Main.res +29 -143
  52. package/src/Main.res.mjs +25 -88
  53. package/src/Metrics.res +1 -1
  54. package/src/Metrics.res.mjs +1 -1
  55. package/src/Persistence.res +12 -3
  56. package/src/PgStorage.res +155 -82
  57. package/src/PgStorage.res.mjs +130 -77
  58. package/src/Prometheus.res +20 -10
  59. package/src/Prometheus.res.mjs +22 -10
  60. package/src/PruneStaleHistory.res +146 -35
  61. package/src/PruneStaleHistory.res.mjs +114 -20
  62. package/src/RawEvent.res +2 -2
  63. package/src/Rollback.res +32 -13
  64. package/src/Rollback.res.mjs +24 -11
  65. package/src/RollbackCommit.res +4 -1
  66. package/src/RollbackCommit.res.mjs +3 -2
  67. package/src/SimulateDeadInputTracker.res +1 -1
  68. package/src/SimulateItems.res +39 -7
  69. package/src/SimulateItems.res.mjs +28 -9
  70. package/src/TestIndexer.res +2 -2
  71. package/src/TestIndexer.res.mjs +2 -2
  72. package/src/TopicFilter.res +1 -25
  73. package/src/TopicFilter.res.mjs +0 -74
  74. package/src/UserContext.res +62 -23
  75. package/src/UserContext.res.mjs +26 -6
  76. package/src/Writing.res +60 -21
  77. package/src/Writing.res.mjs +27 -9
  78. package/src/bindings/NodeJs.res +5 -0
  79. package/src/bindings/Viem.res +0 -5
  80. package/src/sources/EventRouter.res +0 -21
  81. package/src/sources/EventRouter.res.mjs +0 -12
  82. package/src/sources/Evm.res +4 -1
  83. package/src/sources/Evm.res.mjs +1 -1
  84. package/src/sources/EvmChain.res +2 -27
  85. package/src/sources/EvmChain.res.mjs +2 -23
  86. package/src/sources/EvmRpcClient.res +58 -23
  87. package/src/sources/EvmRpcClient.res.mjs +11 -5
  88. package/src/sources/Fuel.res +3 -1
  89. package/src/sources/Fuel.res.mjs +1 -1
  90. package/src/sources/HyperSync.res +9 -38
  91. package/src/sources/HyperSync.res.mjs +16 -28
  92. package/src/sources/HyperSync.resi +2 -2
  93. package/src/sources/HyperSyncClient.res +88 -11
  94. package/src/sources/HyperSyncClient.res.mjs +39 -6
  95. package/src/sources/HyperSyncSource.res +18 -210
  96. package/src/sources/HyperSyncSource.res.mjs +9 -137
  97. package/src/sources/Rpc.res +0 -32
  98. package/src/sources/Rpc.res.mjs +1 -46
  99. package/src/sources/RpcSource.res +129 -533
  100. package/src/sources/RpcSource.res.mjs +161 -379
  101. package/src/sources/SimulateSource.res +37 -19
  102. package/src/sources/SimulateSource.res.mjs +27 -10
  103. package/src/sources/SourceManager.res +4 -14
  104. package/src/sources/SourceManager.res.mjs +3 -9
  105. package/src/sources/SourceManager.resi +0 -2
  106. package/src/sources/SvmHyperSyncSource.res +13 -3
  107. package/src/sources/SvmHyperSyncSource.res.mjs +1 -1
@@ -1,46 +1,140 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
+ import * as Env from "./Env.res.mjs";
3
4
  import * as Utils from "./Utils.res.mjs";
5
+ import * as Config from "./Config.res.mjs";
4
6
  import * as Logging from "./Logging.res.mjs";
5
- import * as Throttler from "./Throttler.res.mjs";
6
7
  import * as Prometheus from "./Prometheus.res.mjs";
7
8
  import * as Performance from "./bindings/Performance.res.mjs";
8
9
  import * as IndexerState from "./IndexerState.res.mjs";
9
- import * as ErrorHandling from "./ErrorHandling.res.mjs";
10
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
10
11
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
11
12
 
12
- async function runPrune(state) {
13
- let safeCheckpointId = IndexerState.getSafeCheckpointId(state);
14
- if (safeCheckpointId === undefined) {
13
+ function selectFrom(allEntities, lastPrunedAtMillis, writtenEntityNames, isRollback, nowMillis, intervalMillis, safeCheckpointId) {
14
+ let byOldestPrune = (param, param$1) => param[0] - param$1[0];
15
+ let toEntities = candidates => candidates.map(param => param[1]);
16
+ let concurrentCandidates = [];
17
+ let forcedCandidates = [];
18
+ allEntities.forEach(entityConfig => {
19
+ if (!entityConfig.storage.postgres) {
20
+ return;
21
+ }
22
+ let lastPrunedAt = Stdlib_Option.getOr(lastPrunedAtMillis[entityConfig.name], 0);
23
+ if (!isRollback && !writtenEntityNames.has(entityConfig.name) && nowMillis - lastPrunedAt >= intervalMillis) {
24
+ concurrentCandidates.push([
25
+ lastPrunedAt,
26
+ entityConfig
27
+ ]);
28
+ return;
29
+ } else if (nowMillis - lastPrunedAt >= intervalMillis * 5) {
30
+ forcedCandidates.push([
31
+ lastPrunedAt,
32
+ entityConfig
33
+ ]);
34
+ return;
35
+ } else {
36
+ return;
37
+ }
38
+ });
39
+ let sortedConcurrent = concurrentCandidates.toSorted(byOldestPrune);
40
+ for (let idx = 5, idx_finish = sortedConcurrent.length; idx < idx_finish; ++idx) {
41
+ let match = sortedConcurrent[idx];
42
+ if (nowMillis - match[0] >= intervalMillis * 5) {
43
+ forcedCandidates.push(sortedConcurrent[idx]);
44
+ }
45
+ }
46
+ return {
47
+ safeCheckpointId: safeCheckpointId,
48
+ concurrent: toEntities(sortedConcurrent.slice(0, 5)),
49
+ forced: toEntities(forcedCandidates.toSorted(byOldestPrune).slice(0, 5))
50
+ };
51
+ }
52
+
53
+ function select(state, writtenEntityNames, isRollback) {
54
+ let config = IndexerState.config(state);
55
+ if (!Config.shouldPruneHistory(config, IndexerState.isInReorgThreshold(state))) {
15
56
  return;
16
57
  }
58
+ let safeCheckpointId = IndexerState.getSafeCheckpointId(state);
59
+ if (safeCheckpointId !== undefined) {
60
+ return selectFrom(IndexerState.persistence(state).allEntities, IndexerState.lastPrunedAtMillis(state), writtenEntityNames, isRollback, Date.now(), Env.ThrottleWrites.pruneStaleDataIntervalMillis, safeCheckpointId);
61
+ }
62
+ }
63
+
64
+ async function pruneEntities(state, entities, safeCheckpointId) {
17
65
  let persistence = IndexerState.persistence(state);
18
- await persistence.storage.pruneStaleCheckpoints(safeCheckpointId);
19
- for (let idx = 0, idx_finish = persistence.allEntities.length; idx < idx_finish; ++idx) {
20
- if (idx !== 0) {
21
- await Utils.delay(1000);
22
- }
23
- let entityConfig = persistence.allEntities[idx];
66
+ for (let idx = 0, idx_finish = entities.length; idx < idx_finish; ++idx) {
67
+ let entityConfig = entities[idx];
24
68
  let timeRef = Performance.now();
69
+ IndexerState.lastPrunedAtMillis(state)[entityConfig.name] = Date.now();
70
+ let exit = 0;
71
+ let val;
25
72
  try {
26
- await persistence.storage.pruneStaleEntityHistory(entityConfig.name, entityConfig.index, safeCheckpointId);
73
+ val = await persistence.storage.pruneStaleEntityHistory(entityConfig.name, entityConfig.index, safeCheckpointId);
74
+ exit = 1;
27
75
  } catch (raw_exn) {
28
76
  let exn = Primitive_exceptions.internalToException(raw_exn);
29
- ErrorHandling.mkLogAndRaise(Logging.createChild({
77
+ Logging.childErrorWithExn(Logging.createChild({
30
78
  entityName: entityConfig.name,
31
79
  safeCheckpointId: safeCheckpointId
32
- }), `Failed to prune stale entity history`, exn);
80
+ }), Utils.prettifyExn(exn), `Failed to prune stale entity history`);
81
+ }
82
+ if (exit === 1) {
83
+ Prometheus.RollbackHistoryPrune.increment(Performance.secondsSince(timeRef), entityConfig.name);
33
84
  }
34
- Prometheus.RollbackHistoryPrune.increment(Performance.secondsSince(timeRef), entityConfig.name);
35
85
  }
36
86
  }
37
87
 
38
- function schedule(state) {
39
- Throttler.schedule(IndexerState.pruneStaleEntityHistoryThrottler(state), () => runPrune(state));
88
+ async function pruneCheckpoints(state, safeCheckpointId) {
89
+ try {
90
+ await IndexerState.persistence(state).storage.pruneStaleCheckpoints(safeCheckpointId);
91
+ return;
92
+ } catch (raw_exn) {
93
+ let exn = Primitive_exceptions.internalToException(raw_exn);
94
+ return Logging.childErrorWithExn(Logging.createChild({
95
+ safeCheckpointId: safeCheckpointId
96
+ }), Utils.prettifyExn(exn), `Failed to prune stale checkpoints`);
97
+ }
98
+ }
99
+
100
+ async function runConcurrent(state, targets) {
101
+ if (targets === undefined) {
102
+ return;
103
+ }
104
+ let concurrent = targets.concurrent;
105
+ let safeCheckpointId = targets.safeCheckpointId;
106
+ if (Utils.$$Array.notEmpty(concurrent)) {
107
+ await pruneCheckpoints(state, safeCheckpointId);
108
+ return await pruneEntities(state, concurrent, safeCheckpointId);
109
+ }
110
+ }
111
+
112
+ async function runForced(state, targets) {
113
+ if (targets === undefined) {
114
+ return;
115
+ }
116
+ let forced = targets.forced;
117
+ let safeCheckpointId = targets.safeCheckpointId;
118
+ if (Utils.$$Array.notEmpty(forced)) {
119
+ if (Utils.$$Array.isEmpty(targets.concurrent)) {
120
+ await pruneCheckpoints(state, safeCheckpointId);
121
+ }
122
+ return await pruneEntities(state, forced, safeCheckpointId);
123
+ }
40
124
  }
41
125
 
126
+ let maxEntitiesPerWrite = 5;
127
+
128
+ let forcedIntervalMultiplier = 5;
129
+
42
130
  export {
43
- runPrune,
44
- schedule,
131
+ maxEntitiesPerWrite,
132
+ forcedIntervalMultiplier,
133
+ selectFrom,
134
+ select,
135
+ pruneEntities,
136
+ pruneCheckpoints,
137
+ runConcurrent,
138
+ runForced,
45
139
  }
46
- /* Utils Not a pure module */
140
+ /* Env Not a pure module */
package/src/RawEvent.res CHANGED
@@ -32,8 +32,8 @@ let make = (
32
32
  ~blockTimestamp: int,
33
33
  ~cleanUpRawEventFieldsInPlace: JSON.t => unit,
34
34
  ): Internal.rawEvent => {
35
- let {onEventRegistration, chain, blockNumber, logIndex} = eventItem
36
- let eventConfig = onEventRegistration.eventConfig
35
+ let {chain, blockNumber, logIndex} = eventItem
36
+ let eventConfig = eventItem.onEventRegistration.eventConfig
37
37
  let chainId = chain->ChainMap.Chain.toChainId
38
38
  let eventId = EventUtils.packEventIndex(~logIndex, ~blockNumber)
39
39
  let blockFields =
package/src/Rollback.res CHANGED
@@ -72,7 +72,7 @@ let rec rollback = async (
72
72
  // found yet. Wait for the ReorgDetected branch above to find it and re-kick.
73
73
  | FindingReorgDepth => ()
74
74
  | FoundReorgDepth(_) if state->IndexerState.isProcessing =>
75
- Logging.info("Waiting for batch to finish processing before executing rollback")
75
+ Logging.trace("Waiting for batch to finish processing before executing rollback")
76
76
  | FoundReorgDepth({chain: reorgChain, rollbackTargetBlockNumber}) =>
77
77
  await executeRollback(
78
78
  state,
@@ -96,10 +96,10 @@ and executeRollback = async (
96
96
  ) => {
97
97
  let startTime = Performance.now()
98
98
 
99
- let chainState = state->IndexerState.getChainState(~chain=reorgChain)
100
-
101
- let logger = Logging.createChildFrom(
102
- ~logger=chainState->ChainState.logger,
99
+ // Not derived from the reorg chain's logger: that would bind its chainId onto
100
+ // every line, colliding with the per-chain chainId on the "Rollbacked" logs.
101
+ // The reorg chain is identified by the reorgChain param instead.
102
+ let logger = Logging.createChild(
103
103
  ~params={
104
104
  "action": "Rollback",
105
105
  "reorgChain": reorgChain,
@@ -161,10 +161,12 @@ and executeRollback = async (
161
161
  }
162
162
  }
163
163
 
164
+ let rolledBackChains = []
164
165
  state
165
166
  ->IndexerState.chainStates
166
167
  ->Utils.Dict.forEach(cs => {
167
168
  let chainId = (cs->ChainState.chainConfig).id
169
+ let fromBlock = cs->ChainState.committedProgressBlockNumber
168
170
  cs->ChainState.rollback(
169
171
  ~newProgressBlockNumber=newProgressBlockNumberPerChain->Utils.Dict.dangerouslyGetByIntNonOption(
170
172
  chainId,
@@ -175,6 +177,19 @@ and executeRollback = async (
175
177
  ~rollbackTargetBlockNumber,
176
178
  ~isReorgChain=chainId === reorgChainId,
177
179
  )
180
+ let toBlock = cs->ChainState.committedProgressBlockNumber
181
+ if fromBlock !== toBlock {
182
+ rolledBackChains
183
+ ->Array.push({
184
+ "chainId": chainId,
185
+ "fromBlock": fromBlock,
186
+ "toBlock": toBlock,
187
+ "rollbackedEvents": eventsProcessedDiffByChain
188
+ ->Utils.Dict.dangerouslyGetByIntNonOption(chainId)
189
+ ->Option.getOr(0.),
190
+ })
191
+ ->ignore
192
+ }
178
193
  })
179
194
 
180
195
  let diff = await state->InMemoryStore.prepareRollbackDiff(
@@ -183,15 +198,19 @@ and executeRollback = async (
183
198
  ~progressBlockNumberByChainId=newProgressBlockNumberPerChain,
184
199
  )
185
200
 
201
+ rolledBackChains->Array.forEach(chain => {
202
+ logger->Logging.childInfo({
203
+ "msg": "Rollbacked",
204
+ "chainId": chain["chainId"],
205
+ "fromBlock": chain["fromBlock"],
206
+ "toBlock": chain["toBlock"],
207
+ "rollbackedEvents": chain["rollbackedEvents"],
208
+ })
209
+ })
186
210
  logger->Logging.childTrace({
187
- "msg": "Finished rollback on reorg",
188
- "entityChanges": {
189
- "deleted": diff["deletedEntities"],
190
- "upserted": diff["setEntities"],
191
- },
192
- "rollbackedEvents": rollbackedProcessedEvents.contents,
193
- "beforeCheckpointId": state->IndexerState.committedCheckpointId,
194
- "targetCheckpointId": rollbackTargetCheckpointId,
211
+ "msg": "Rollback entity changes",
212
+ "deleted": diff["deletedEntities"],
213
+ "upserted": diff["setEntities"],
195
214
  })
196
215
  Prometheus.RollbackSuccess.increment(
197
216
  ~timeSeconds=Performance.secondsSince(startTime),
@@ -33,8 +33,7 @@ async function getLastKnownValidBlock(chainState, reorgBlockNumber, isRealtime)
33
33
 
34
34
  async function executeRollback(state, reorgChain, rollbackTargetBlockNumber, scheduleFetch, scheduleProcessing) {
35
35
  let startTime = Performance.now();
36
- let chainState = IndexerState.getChainState(state, reorgChain);
37
- let logger = Logging.createChildFrom(ChainState.logger(chainState), {
36
+ let logger = Logging.createChild({
38
37
  action: "Rollback",
39
38
  reorgChain: reorgChain,
40
39
  targetBlockNumber: rollbackTargetBlockNumber
@@ -55,20 +54,34 @@ async function executeRollback(state, reorgChain, rollbackTargetBlockNumber, sch
55
54
  eventsProcessedDiffByChain[diff.chain_id] = eventsProcessedDiff;
56
55
  newProgressBlockNumberPerChain[diff.chain_id] = rollbackTargetCheckpointId === 0n && diff.chain_id === reorgChain ? Primitive_int.min(diff.new_progress_block_number, rollbackTargetBlockNumber) : diff.new_progress_block_number;
57
56
  }
57
+ let rolledBackChains = [];
58
58
  Utils.Dict.forEach(IndexerState.chainStates(state), cs => {
59
59
  let chainId = ChainState.chainConfig(cs).id;
60
+ let fromBlock = ChainState.committedProgressBlockNumber(cs);
60
61
  ChainState.rollback(cs, newProgressBlockNumberPerChain[chainId], eventsProcessedDiffByChain[chainId], rollbackTargetBlockNumber, chainId === reorgChain);
62
+ let toBlock = ChainState.committedProgressBlockNumber(cs);
63
+ if (fromBlock !== toBlock) {
64
+ rolledBackChains.push({
65
+ chainId: chainId,
66
+ fromBlock: fromBlock,
67
+ toBlock: toBlock,
68
+ rollbackedEvents: Stdlib_Option.getOr(eventsProcessedDiffByChain[chainId], 0)
69
+ });
70
+ return;
71
+ }
61
72
  });
62
73
  let diff$1 = await InMemoryStore.prepareRollbackDiff(state, rollbackTargetCheckpointId, IndexerState.committedCheckpointId(state) + 1n, newProgressBlockNumberPerChain);
74
+ rolledBackChains.forEach(chain => Logging.childInfo(logger, {
75
+ msg: "Rollbacked",
76
+ chainId: chain.chainId,
77
+ fromBlock: chain.fromBlock,
78
+ toBlock: chain.toBlock,
79
+ rollbackedEvents: chain.rollbackedEvents
80
+ }));
63
81
  Logging.childTrace(logger, {
64
- msg: "Finished rollback on reorg",
65
- entityChanges: {
66
- deleted: diff$1.deletedEntities,
67
- upserted: diff$1.setEntities
68
- },
69
- rollbackedEvents: rollbackedProcessedEvents,
70
- beforeCheckpointId: IndexerState.committedCheckpointId(state),
71
- targetCheckpointId: rollbackTargetCheckpointId
82
+ msg: "Rollback entity changes",
83
+ deleted: diff$1.deletedEntities,
84
+ upserted: diff$1.setEntities
72
85
  });
73
86
  Prometheus.RollbackSuccess.increment(Performance.secondsSince(startTime), rollbackedProcessedEvents);
74
87
  IndexerState.completeRollback(state, eventsProcessedDiffByChain);
@@ -97,7 +110,7 @@ async function rollback(state, scheduleFetch, scheduleProcessing, scheduleRollba
97
110
  return scheduleRollback();
98
111
  case "FoundReorgDepth" :
99
112
  if (IndexerState.isProcessing(state)) {
100
- return Logging.info("Waiting for batch to finish processing before executing rollback");
113
+ return Logging.trace("Waiting for batch to finish processing before executing rollback");
101
114
  } else {
102
115
  return await executeRollback(state, match.chain, match.rollbackTargetBlockNumber, scheduleFetch, scheduleProcessing);
103
116
  }
@@ -6,7 +6,10 @@
6
6
  type args = {chainId: int, rollbackToBlock: int}
7
7
  type callback = args => promise<unit>
8
8
 
9
- let callbacks: array<callback> = []
9
+ // Lives in the process-wide `EnvioGlobal` record so callbacks registered
10
+ // through a duplicate envio module instance still fire.
11
+ let callbacks =
12
+ EnvioGlobal.value.rollbackCommitCallbacks->(Utils.magic: array<unknown> => array<callback>)
10
13
 
11
14
  let register = (callback: callback) => {
12
15
  callbacks->Array.push(callback)
@@ -1,8 +1,9 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
3
  import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
4
+ import * as EnvioGlobal from "./EnvioGlobal.res.mjs";
4
5
 
5
- let callbacks = [];
6
+ let callbacks = EnvioGlobal.value.rollbackCommitCallbacks;
6
7
 
7
8
  function register(callback) {
8
9
  callbacks.push(callback);
@@ -32,4 +33,4 @@ export {
32
33
  register,
33
34
  fire,
34
35
  }
35
- /* No side effect */
36
+ /* EnvioGlobal Not a pure module */
@@ -10,7 +10,7 @@ let itemKey = (item: Internal.item): string =>
10
10
  `${chain
11
11
  ->ChainMap.Chain.toChainId
12
12
  ->Int.toString}:${blockNumber->Int.toString}:${logIndex->Int.toString}`
13
- | _ => "" // non-event items are never a provided simulate input
13
+ | Internal.Block(_) => ""
14
14
  }
15
15
 
16
16
  // `index` is the item's position in its chain's `simulate` array, reported back
@@ -240,9 +240,12 @@ let deriveSrcAddress = (
240
240
  }
241
241
  }
242
242
 
243
- let parse = (~simulateItems: array<JSON.t>, ~config: Config.t, ~chainConfig: Config.chain): array<
244
- Internal.item,
245
- > => {
243
+ let parse = (
244
+ ~simulateItems: array<JSON.t>,
245
+ ~config: Config.t,
246
+ ~chainConfig: Config.chain,
247
+ ~onEventRegistrations: array<Internal.onEventRegistration>,
248
+ ): array<Internal.item> => {
246
249
  let chain = ChainMap.Chain.makeUnsafe(~chainId=chainConfig.id)
247
250
  let chainId = chainConfig.id
248
251
  let startBlock = chainConfig.startBlock
@@ -336,7 +339,7 @@ let parse = (~simulateItems: array<JSON.t>, ~config: Config.t, ~chainConfig: Con
336
339
  | None => seenCoordinates->Dict.set(coordinate, itemIndex)
337
340
  }
338
341
 
339
- // Build a real registration the same way `HandlerRegister.buildOnEventRegistrations`
342
+ // Build a real registration the same way `HandlerRegister.finishRegistration`
340
343
  // does at startup (not a stub), so the address filter and `where`
341
344
  // behave identically to real indexing — the dead-input tracker relies
342
345
  // on `clientAddressFilter` actually gating unrouted items.
@@ -345,6 +348,11 @@ let parse = (~simulateItems: array<JSON.t>, ~config: Config.t, ~chainConfig: Con
345
348
  ~chainId,
346
349
  ~eventConfig,
347
350
  )
351
+ // Append into the registration array that the chain state will own and
352
+ // put that same registration object directly on the simulated item.
353
+ let onEventRegistrationIndex = onEventRegistrations->Array.length
354
+ let onEventRegistration = {...onEventRegistration, index: onEventRegistrationIndex}
355
+ onEventRegistrations->Array.push(onEventRegistration)->ignore
348
356
 
349
357
  items
350
358
  ->Array.push(
@@ -382,7 +390,11 @@ let parse = (~simulateItems: array<JSON.t>, ~config: Config.t, ~chainConfig: Con
382
390
 
383
391
  // Apply simulate source config from processConfig JSON to a Config.t
384
392
  // This patches chainMap entries that have simulate items with CustomSources
385
- let patchConfig = (~config: Config.t, ~processConfig: JSON.t): Config.t => {
393
+ let patchConfig = (
394
+ ~config: Config.t,
395
+ ~processConfig: JSON.t,
396
+ ~registrationsByChainId: HandlerRegister.registrationsByChainId,
397
+ ): Config.t => {
386
398
  let processChains: option<dict<JSON.t>> =
387
399
  (processConfig->(Utils.magic: JSON.t => {..}))["chains"]->Nullable.toOption
388
400
  switch processChains {
@@ -395,11 +407,31 @@ let patchConfig = (~config: Config.t, ~processConfig: JSON.t): Config.t => {
395
407
  let simulateRaw: option<array<JSON.t>> = raw["simulate"]->Nullable.toOption
396
408
  switch simulateRaw {
397
409
  | Some(simulateItems) =>
398
- let items = parse(~simulateItems, ~config, ~chainConfig)
410
+ let chainRegistrations = switch registrationsByChainId->Utils.Dict.dangerouslyGetNonOption(
411
+ chainIdStr,
412
+ ) {
413
+ | Some(registrations) => registrations
414
+ | None =>
415
+ let registrations: HandlerRegister.chainRegistrations = {
416
+ onEventRegistrations: [],
417
+ onBlockRegistrations: [],
418
+ }
419
+ registrationsByChainId->Dict.set(chainIdStr, registrations)
420
+ registrations
421
+ }
399
422
  let startBlock: int = raw["startBlock"]->(Utils.magic: 'a => int)
400
423
  let endBlock: int = raw["endBlock"]->(Utils.magic: 'a => int)
424
+ // Parse with the process's startBlock so items default into the range
425
+ // the source will be queried over; the source now filters by range.
426
+ let chainConfig = {...chainConfig, startBlock, endBlock}
427
+ let items = parse(
428
+ ~simulateItems,
429
+ ~config,
430
+ ~chainConfig,
431
+ ~onEventRegistrations=chainRegistrations.onEventRegistrations,
432
+ )
401
433
  let source = SimulateSource.make(~items, ~endBlock, ~chain)
402
- {...chainConfig, startBlock, endBlock, sourceConfig: Config.CustomSources([source])}
434
+ {...chainConfig, sourceConfig: Config.CustomSources([source])}
403
435
  | None => chainConfig
404
436
  }
405
437
  | None => chainConfig
@@ -167,7 +167,7 @@ function deriveSrcAddress(providedSrcAddress, eventConfig, chainConfig, config)
167
167
  }
168
168
  }
169
169
 
170
- function parse(simulateItems, config, chainConfig) {
170
+ function parse(simulateItems, config, chainConfig, onEventRegistrations) {
171
171
  let chain = ChainMap.Chain.makeUnsafe(chainConfig.id);
172
172
  let chainId = chainConfig.id;
173
173
  let startBlock = chainConfig.startBlock;
@@ -254,9 +254,13 @@ function parse(simulateItems, config, chainConfig) {
254
254
  seenCoordinates[coordinate] = itemIndex;
255
255
  }
256
256
  let onEventRegistration = HandlerRegister.buildOnEventRegistration(config, chainId, eventConfig, undefined);
257
+ let onEventRegistrationIndex = onEventRegistrations.length;
258
+ let newrecord = {...onEventRegistration};
259
+ newrecord.index = onEventRegistrationIndex;
260
+ onEventRegistrations.push(newrecord);
257
261
  items.push({
258
262
  kind: 0,
259
- onEventRegistration: onEventRegistration,
263
+ onEventRegistration: newrecord,
260
264
  chain: chain,
261
265
  blockNumber: blockNumber,
262
266
  logIndex: logIndex,
@@ -276,7 +280,7 @@ function parse(simulateItems, config, chainConfig) {
276
280
  return items;
277
281
  }
278
282
 
279
- function patchConfig(config, processConfig) {
283
+ function patchConfig(config, processConfig, registrationsByChainId) {
280
284
  let processChains = processConfig.chains;
281
285
  if (processChains == null) {
282
286
  return config;
@@ -291,18 +295,33 @@ function patchConfig(config, processConfig) {
291
295
  if (simulateRaw == null) {
292
296
  return chainConfig;
293
297
  }
294
- let items = parse(simulateRaw, config, chainConfig);
298
+ let registrations = registrationsByChainId[chainIdStr];
299
+ let chainRegistrations;
300
+ if (registrations !== undefined) {
301
+ chainRegistrations = registrations;
302
+ } else {
303
+ let registrations_onEventRegistrations = [];
304
+ let registrations_onBlockRegistrations = [];
305
+ let registrations$1 = {
306
+ onEventRegistrations: registrations_onEventRegistrations,
307
+ onBlockRegistrations: registrations_onBlockRegistrations
308
+ };
309
+ registrationsByChainId[chainIdStr] = registrations$1;
310
+ chainRegistrations = registrations$1;
311
+ }
295
312
  let startBlock = processChainJson.startBlock;
296
313
  let endBlock = processChainJson.endBlock;
297
- let source = SimulateSource.make(items, endBlock, chain);
298
314
  let newrecord = {...chainConfig};
299
- newrecord.sourceConfig = {
315
+ newrecord.endBlock = endBlock;
316
+ newrecord.startBlock = startBlock;
317
+ let items = parse(simulateRaw, config, newrecord, chainRegistrations.onEventRegistrations);
318
+ let source = SimulateSource.make(items, endBlock, chain);
319
+ let newrecord$1 = {...newrecord};
320
+ newrecord$1.sourceConfig = {
300
321
  TAG: "CustomSources",
301
322
  _0: [source]
302
323
  };
303
- newrecord.endBlock = endBlock;
304
- newrecord.startBlock = startBlock;
305
- return newrecord;
324
+ return newrecord$1;
306
325
  });
307
326
  return {
308
327
  name: config.name,
@@ -863,8 +863,8 @@ let initTestWorker = () => {
863
863
  | Some(_) => ()
864
864
  }
865
865
 
866
- let patchConfig = (config: Config.t, _registrations) => {
867
- let config = SimulateItems.patchConfig(~config, ~processConfig)
866
+ let patchConfig = (config: Config.t, registrationsByChainId) => {
867
+ let config = SimulateItems.patchConfig(~config, ~processConfig, ~registrationsByChainId)
868
868
 
869
869
  // In auto-exit mode, set batchSize=1 to process one block checkpoint at a time
870
870
  if exitAfterFirstEventBlock {
@@ -612,8 +612,8 @@ function initTestWorker() {
612
612
  } else {
613
613
  Logging.setLogLevel("silent");
614
614
  }
615
- let patchConfig = (config, _registrations) => {
616
- let config$1 = SimulateItems.patchConfig(config, processConfig);
615
+ let patchConfig = (config, registrationsByChainId) => {
616
+ let config$1 = SimulateItems.patchConfig(config, processConfig, registrationsByChainId);
617
617
  if (exitAfterFirstEventBlock) {
618
618
  return {
619
619
  name: config$1.name,
@@ -1,27 +1,3 @@
1
- let toTwosComplement = (num: bigint, ~bytesLen: int) => {
2
- let maxValue = 1n->BigInt.shiftLeft(BigInt.fromInt(bytesLen * 8))
3
- let mask = maxValue->BigInt.sub(1n)
4
- num->BigInt.add(maxValue)->BigInt.bitwiseAnd(mask)
5
- }
6
-
7
- let fromSignedBigInt = val => {
8
- let bytesLen = 32
9
- let val = val >= 0n ? val : val->toTwosComplement(~bytesLen)
10
- val->Viem.bigintToHex(~options={size: bytesLen})
11
- }
12
-
13
1
  type hex = EvmTypes.Hex.t
14
- //bytes currently does not work with genType and we also currently generate bytes as a string type
15
- type bytesHex = string
16
- let keccak256 = Viem.keccak256
17
- let bytesToHex = Viem.bytesToHex
18
- let concat = Viem.concat
19
- let castToHexUnsafe: 'a => hex = val => val->(Utils.magic: 'a => hex)
20
- let fromBigInt: bigint => hex = val => val->Viem.bigintToHex(~options={size: 32})
21
- let fromDynamicString: string => hex = val => val->(Utils.magic: string => hex)->keccak256
22
- let fromString: string => hex = val => val->Viem.stringToHex(~options={size: 32})
2
+
23
3
  let fromAddress: Address.t => hex = addr => addr->(Utils.magic: Address.t => hex)->Viem.pad
24
- let fromDynamicBytes: bytesHex => hex = bytes => bytes->(Utils.magic: bytesHex => hex)->keccak256
25
- let fromBytes: bytesHex => hex = bytes =>
26
- bytes->(Utils.magic: bytesHex => Uint8Array.t)->Viem.bytesToHex(~options={size: 32})
27
- let fromBool: bool => hex = b => b->Viem.boolToHex(~options={size: 32})
@@ -1,86 +1,12 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
3
  import * as Viem from "viem";
4
- import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
5
-
6
- function toTwosComplement(num, bytesLen) {
7
- let maxValue = (1n << BigInt((bytesLen << 3)));
8
- let mask = maxValue - 1n;
9
- return num + maxValue & mask;
10
- }
11
-
12
- function fromSignedBigInt(val) {
13
- let val$1 = val >= 0n ? val : toTwosComplement(val, 32);
14
- return Viem.numberToHex(val$1, {
15
- size: 32
16
- });
17
- }
18
-
19
- function keccak256(prim) {
20
- return Viem.keccak256(prim);
21
- }
22
-
23
- function bytesToHex(prim0, prim1) {
24
- return Viem.bytesToHex(prim0, prim1 !== undefined ? Primitive_option.valFromOption(prim1) : undefined);
25
- }
26
-
27
- function concat(prim) {
28
- return Viem.concat(prim);
29
- }
30
-
31
- function castToHexUnsafe(val) {
32
- return val;
33
- }
34
-
35
- function fromBigInt(val) {
36
- return Viem.numberToHex(val, {
37
- size: 32
38
- });
39
- }
40
-
41
- function fromDynamicString(val) {
42
- return Viem.keccak256(val);
43
- }
44
-
45
- function fromString(val) {
46
- return Viem.stringToHex(val, {
47
- size: 32
48
- });
49
- }
50
4
 
51
5
  function fromAddress(addr) {
52
6
  return Viem.pad(addr);
53
7
  }
54
8
 
55
- function fromDynamicBytes(bytes) {
56
- return Viem.keccak256(bytes);
57
- }
58
-
59
- function fromBytes(bytes) {
60
- return Viem.bytesToHex(bytes, {
61
- size: 32
62
- });
63
- }
64
-
65
- function fromBool(b) {
66
- return Viem.boolToHex(b, {
67
- size: 32
68
- });
69
- }
70
-
71
9
  export {
72
- toTwosComplement,
73
- fromSignedBigInt,
74
- keccak256,
75
- bytesToHex,
76
- concat,
77
- castToHexUnsafe,
78
- fromBigInt,
79
- fromDynamicString,
80
- fromString,
81
10
  fromAddress,
82
- fromDynamicBytes,
83
- fromBytes,
84
- fromBool,
85
11
  }
86
12
  /* viem Not a pure module */