envio 3.5.0-alpha.2 → 3.5.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/package.json +6 -6
- package/src/Batch.res +8 -8
- package/src/Batch.res.mjs +4 -3
- package/src/BatchProcessing.res +24 -0
- package/src/BatchProcessing.res.mjs +12 -0
- package/src/ChainFetching.res +20 -19
- package/src/ChainFetching.res.mjs +14 -14
- package/src/ChainId.res +58 -0
- package/src/ChainId.res.mjs +49 -0
- package/src/ChainId.resi +27 -0
- package/src/ChainMap.res +12 -26
- package/src/ChainMap.res.mjs +5 -41
- package/src/ChainMap.resi +5 -20
- package/src/ChainMetadata.res +1 -1
- package/src/ChainMetadata.res.mjs +3 -2
- package/src/ChainState.res +45 -24
- package/src/ChainState.res.mjs +40 -16
- package/src/ChainState.resi +3 -1
- package/src/Config.res +43 -26
- package/src/Config.res.mjs +19 -16
- package/src/ContractRegisterContext.res +1 -1
- package/src/ContractRegisterContext.res.mjs +1 -1
- package/src/Core.res +1 -0
- package/src/CrossChainState.res +90 -22
- package/src/CrossChainState.res.mjs +55 -16
- package/src/CrossChainState.resi +5 -1
- package/src/Env.res +0 -3
- package/src/Env.res.mjs +0 -3
- package/src/EventConfigBuilder.res +1 -1
- package/src/EventProcessing.res +4 -4
- package/src/EventProcessing.res.mjs +5 -5
- package/src/FetchState.res +489 -153
- package/src/FetchState.res.mjs +399 -129
- package/src/FinalizeBackfill.res +61 -0
- package/src/FinalizeBackfill.res.mjs +44 -0
- package/src/HandlerRegister.res +8 -8
- package/src/HandlerRegister.res.mjs +5 -4
- package/src/HandlerRegister.resi +1 -1
- package/src/InMemoryStore.res +4 -0
- package/src/InMemoryStore.res.mjs +1 -1
- package/src/InMemoryTable.res +15 -15
- package/src/InMemoryTable.res.mjs +14 -14
- package/src/IndexerLoop.res +9 -2
- package/src/IndexerLoop.res.mjs +8 -1
- package/src/IndexerState.res +52 -23
- package/src/IndexerState.res.mjs +52 -14
- package/src/IndexerState.resi +11 -6
- package/src/Internal.res +19 -17
- package/src/Internal.res.mjs +18 -9
- package/src/LoadLayer.res +10 -2
- package/src/LoadLayer.res.mjs +3 -1
- package/src/LogSelection.res +2 -2
- package/src/Main.res +7 -11
- package/src/Main.res.mjs +4 -6
- package/src/Metrics.res +6 -6
- package/src/Metrics.res.mjs +4 -3
- package/src/Persistence.res +23 -4
- package/src/PgStorage.res +447 -100
- package/src/PgStorage.res.mjs +285 -99
- package/src/RawEvent.res +1 -2
- package/src/RawEvent.res.mjs +1 -1
- package/src/Rollback.res +18 -20
- package/src/Rollback.res.mjs +9 -9
- package/src/RollbackCommit.res +2 -2
- package/src/RollbackCommit.res.mjs +3 -3
- package/src/SafeCheckpointTracking.res +2 -2
- package/src/SimulateDeadInputTracker.res +17 -21
- package/src/SimulateDeadInputTracker.res.mjs +9 -8
- package/src/SimulateItems.res +8 -10
- package/src/SimulateItems.res.mjs +9 -10
- package/src/Sink.res +2 -2
- package/src/Sink.res.mjs +3 -2
- package/src/TestIndexer.res +14 -14
- package/src/TestIndexer.res.mjs +16 -10
- package/src/UserContext.res +2 -2
- package/src/UserContext.res.mjs +2 -1
- package/src/Utils.res +0 -4
- package/src/Utils.res.mjs +1 -6
- package/src/bindings/ClickHouse.res +20 -2
- package/src/bindings/ClickHouse.res.mjs +25 -14
- package/src/db/EntityFilter.res +0 -4
- package/src/db/EntityFilter.res.mjs +1 -8
- package/src/db/EntityHistory.res +1 -1
- package/src/db/IndexCatalog.res +248 -0
- package/src/db/IndexCatalog.res.mjs +222 -0
- package/src/db/IndexDefinition.res +131 -0
- package/src/db/IndexDefinition.res.mjs +138 -0
- package/src/db/IndexManager.res +145 -0
- package/src/db/IndexManager.res.mjs +130 -0
- package/src/db/InternalTable.res +54 -23
- package/src/db/InternalTable.res.mjs +38 -23
- package/src/db/Table.res +24 -14
- package/src/db/Table.res.mjs +21 -16
- package/src/sources/AddressSet.res +5 -4
- package/src/sources/AddressStore.res +25 -14
- package/src/sources/AddressStore.res.mjs +10 -12
- package/src/sources/Evm.res +2 -2
- package/src/sources/Evm.res.mjs +1 -1
- package/src/sources/EvmChain.res +3 -3
- package/src/sources/EvmChain.res.mjs +3 -3
- package/src/sources/EvmHyperSyncSource.res +7 -7
- package/src/sources/EvmHyperSyncSource.res.mjs +6 -6
- package/src/sources/Fuel.res +2 -2
- package/src/sources/Fuel.res.mjs +1 -1
- package/src/sources/FuelHyperSyncClient.res +3 -0
- package/src/sources/FuelHyperSyncClient.res.mjs +1 -0
- package/src/sources/FuelHyperSyncSource.res +5 -7
- package/src/sources/FuelHyperSyncSource.res.mjs +5 -5
- package/src/sources/HyperSync.resi +2 -2
- package/src/sources/HyperSyncClient.res +5 -0
- package/src/sources/HyperSyncClient.res.mjs +1 -0
- package/src/sources/RpcSource.res +13 -14
- package/src/sources/RpcSource.res.mjs +13 -12
- package/src/sources/SimulateSource.res +42 -15
- package/src/sources/SimulateSource.res.mjs +27 -9
- package/src/sources/Source.res +1 -5
- package/src/sources/SourceManager.res +12 -12
- package/src/sources/SourceManager.res.mjs +11 -10
- package/src/sources/SourceManager.resi +2 -2
- package/src/sources/Svm.res +4 -5
- package/src/sources/Svm.res.mjs +5 -4
- package/src/sources/SvmHyperSyncClient.res +3 -0
- package/src/sources/SvmHyperSyncClient.res.mjs +1 -0
- package/src/sources/SvmHyperSyncSource.res +4 -4
- package/src/sources/SvmHyperSyncSource.res.mjs +3 -3
- package/src/tui/Tui.res +1 -1
- package/src/tui/Tui.res.mjs +2 -1
- package/src/tui/components/CustomHooks.res +5 -4
- package/src/tui/components/CustomHooks.res.mjs +4 -2
package/src/RawEvent.res.mjs
CHANGED
|
@@ -29,7 +29,7 @@ function make(eventItem, block, transaction, params, srcAddress, blockHash, bloc
|
|
|
29
29
|
let params$1 = S$RescriptSchema.reverseConvertOrThrow(params, eventConfig.paramsRawEventSchema);
|
|
30
30
|
let params$2 = params$1 === null ? "null" : params$1;
|
|
31
31
|
return {
|
|
32
|
-
chain_id: eventItem.
|
|
32
|
+
chain_id: eventItem.chainId,
|
|
33
33
|
event_id: eventId,
|
|
34
34
|
event_name: eventConfig.name,
|
|
35
35
|
contract_name: eventConfig.contractName,
|
package/src/Rollback.res
CHANGED
|
@@ -50,8 +50,8 @@ let rec rollback = async (
|
|
|
50
50
|
switch state->IndexerState.rollbackState {
|
|
51
51
|
| NoRollback | RollbackReady(_) =>
|
|
52
52
|
JsError.throwWithMessage("Internal error: Rollback initiated with invalid state")
|
|
53
|
-
| ReorgDetected({
|
|
54
|
-
let chainState = state->IndexerState.getChainState(~
|
|
53
|
+
| ReorgDetected({chainId, blockNumber: reorgBlockNumber}) =>
|
|
54
|
+
let chainState = state->IndexerState.getChainState(~chainId)
|
|
55
55
|
|
|
56
56
|
state->IndexerState.enterFindingReorgDepth
|
|
57
57
|
let rollbackTargetBlockNumber = await chainState->getLastKnownValidBlock(
|
|
@@ -63,7 +63,7 @@ let rec rollback = async (
|
|
|
63
63
|
->ChainState.sourceManager
|
|
64
64
|
->SourceManager.onReorg(~rollbackTargetBlock=rollbackTargetBlockNumber)
|
|
65
65
|
|
|
66
|
-
state->IndexerState.foundReorgDepth(~
|
|
66
|
+
state->IndexerState.foundReorgDepth(~chainId, ~rollbackTargetBlockNumber)
|
|
67
67
|
// Rendezvous with the processing loop: whichever of {depth found, loop
|
|
68
68
|
// idle} happens last triggers the rollback; the earlier one finds the
|
|
69
69
|
// other condition unmet and bails here.
|
|
@@ -73,7 +73,7 @@ let rec rollback = async (
|
|
|
73
73
|
| FindingReorgDepth => ()
|
|
74
74
|
| FoundReorgDepth(_) if state->IndexerState.isProcessing =>
|
|
75
75
|
Logging.trace("Waiting for batch to finish processing before executing rollback")
|
|
76
|
-
| FoundReorgDepth({
|
|
76
|
+
| FoundReorgDepth({chainId: reorgChain, rollbackTargetBlockNumber}) =>
|
|
77
77
|
await executeRollback(
|
|
78
78
|
state,
|
|
79
79
|
~reorgChain,
|
|
@@ -108,11 +108,9 @@ and executeRollback = async (
|
|
|
108
108
|
)
|
|
109
109
|
logger->Logging.childInfo("Started rollback on reorg")
|
|
110
110
|
state
|
|
111
|
-
->IndexerState.getChainState(~
|
|
111
|
+
->IndexerState.getChainState(~chainId=reorgChain)
|
|
112
112
|
->ChainState.setRollbackTargetBlock(~blockNumber=rollbackTargetBlockNumber)
|
|
113
113
|
|
|
114
|
-
let reorgChainId = reorgChain->ChainMap.Chain.toChainId
|
|
115
|
-
|
|
116
114
|
// Finish pending batch writes first: the target checkpoint, the progress
|
|
117
115
|
// diff and the rollback diff below must all be computed from the same db
|
|
118
116
|
// state. Otherwise an in-flight batch lands after the progress reads and
|
|
@@ -122,7 +120,7 @@ and executeRollback = async (
|
|
|
122
120
|
|
|
123
121
|
let rollbackTargetCheckpointId = {
|
|
124
122
|
switch await (state->IndexerState.persistence).storage.getRollbackTargetCheckpoint(
|
|
125
|
-
~reorgChainId,
|
|
123
|
+
~reorgChainId=reorgChain,
|
|
126
124
|
~lastKnownValidBlockNumber=rollbackTargetBlockNumber,
|
|
127
125
|
) {
|
|
128
126
|
| Some(checkpointId) => checkpointId
|
|
@@ -140,7 +138,7 @@ and executeRollback = async (
|
|
|
140
138
|
).storage.getRollbackProgressDiff(~rollbackTargetCheckpointId)
|
|
141
139
|
for idx in 0 to rollbackProgressDiff->Array.length - 1 {
|
|
142
140
|
let diff = rollbackProgressDiff->Array.getUnsafe(idx)
|
|
143
|
-
eventsProcessedDiffByChain->
|
|
141
|
+
eventsProcessedDiffByChain->ChainId.Dict.set(
|
|
144
142
|
diff["chain_id"],
|
|
145
143
|
{
|
|
146
144
|
let eventsProcessedDiff =
|
|
@@ -149,9 +147,9 @@ and executeRollback = async (
|
|
|
149
147
|
eventsProcessedDiff
|
|
150
148
|
},
|
|
151
149
|
)
|
|
152
|
-
newProgressBlockNumberPerChain->
|
|
150
|
+
newProgressBlockNumberPerChain->ChainId.Dict.set(
|
|
153
151
|
diff["chain_id"],
|
|
154
|
-
if rollbackTargetCheckpointId === 0n && diff["chain_id"] ===
|
|
152
|
+
if rollbackTargetCheckpointId === 0n && diff["chain_id"] === reorgChain {
|
|
155
153
|
Pervasives.min(diff["new_progress_block_number"], rollbackTargetBlockNumber)
|
|
156
154
|
} else {
|
|
157
155
|
diff["new_progress_block_number"]
|
|
@@ -167,14 +165,14 @@ and executeRollback = async (
|
|
|
167
165
|
let chainId = (cs->ChainState.chainConfig).id
|
|
168
166
|
let fromBlock = cs->ChainState.committedProgressBlockNumber
|
|
169
167
|
cs->ChainState.rollback(
|
|
170
|
-
~newProgressBlockNumber=newProgressBlockNumberPerChain->
|
|
168
|
+
~newProgressBlockNumber=newProgressBlockNumberPerChain->ChainId.Dict.dangerouslyGetNonOption(
|
|
171
169
|
chainId,
|
|
172
170
|
),
|
|
173
|
-
~eventsProcessedDiff=eventsProcessedDiffByChain->
|
|
171
|
+
~eventsProcessedDiff=eventsProcessedDiffByChain->ChainId.Dict.dangerouslyGetNonOption(
|
|
174
172
|
chainId,
|
|
175
173
|
),
|
|
176
174
|
~rollbackTargetBlockNumber,
|
|
177
|
-
~isReorgChain=chainId ===
|
|
175
|
+
~isReorgChain=chainId === reorgChain,
|
|
178
176
|
)
|
|
179
177
|
let toBlock = cs->ChainState.committedProgressBlockNumber
|
|
180
178
|
if fromBlock !== toBlock {
|
|
@@ -184,7 +182,7 @@ and executeRollback = async (
|
|
|
184
182
|
"fromBlock": fromBlock,
|
|
185
183
|
"toBlock": toBlock,
|
|
186
184
|
"rollbackedEvents": eventsProcessedDiffByChain
|
|
187
|
-
->
|
|
185
|
+
->ChainId.Dict.dangerouslyGetNonOption(chainId)
|
|
188
186
|
->Option.getOr(0.),
|
|
189
187
|
})
|
|
190
188
|
->ignore
|
|
@@ -197,13 +195,13 @@ and executeRollback = async (
|
|
|
197
195
|
~progressBlockNumberByChainId=newProgressBlockNumberPerChain,
|
|
198
196
|
)
|
|
199
197
|
|
|
200
|
-
rolledBackChains->Array.forEach(
|
|
198
|
+
rolledBackChains->Array.forEach(rolledBack => {
|
|
201
199
|
logger->Logging.childInfo({
|
|
202
200
|
"msg": "Rollbacked",
|
|
203
|
-
"chainId":
|
|
204
|
-
"fromBlock":
|
|
205
|
-
"toBlock":
|
|
206
|
-
"rollbackedEvents":
|
|
201
|
+
"chainId": rolledBack["chainId"],
|
|
202
|
+
"fromBlock": rolledBack["fromBlock"],
|
|
203
|
+
"toBlock": rolledBack["toBlock"],
|
|
204
|
+
"rollbackedEvents": rolledBack["rollbackedEvents"],
|
|
207
205
|
})
|
|
208
206
|
})
|
|
209
207
|
logger->Logging.childTrace({
|
package/src/Rollback.res.mjs
CHANGED
|
@@ -70,12 +70,12 @@ async function executeRollback(state, reorgChain, rollbackTargetBlockNumber, sch
|
|
|
70
70
|
}
|
|
71
71
|
});
|
|
72
72
|
let diff$1 = await InMemoryStore.prepareRollbackDiff(state, rollbackTargetCheckpointId, IndexerState.committedCheckpointId(state) + 1n, newProgressBlockNumberPerChain);
|
|
73
|
-
rolledBackChains.forEach(
|
|
73
|
+
rolledBackChains.forEach(rolledBack => Logging.childInfo(logger, {
|
|
74
74
|
msg: "Rollbacked",
|
|
75
|
-
chainId:
|
|
76
|
-
fromBlock:
|
|
77
|
-
toBlock:
|
|
78
|
-
rollbackedEvents:
|
|
75
|
+
chainId: rolledBack.chainId,
|
|
76
|
+
fromBlock: rolledBack.fromBlock,
|
|
77
|
+
toBlock: rolledBack.toBlock,
|
|
78
|
+
rollbackedEvents: rolledBack.rollbackedEvents
|
|
79
79
|
}));
|
|
80
80
|
Logging.childTrace(logger, {
|
|
81
81
|
msg: "Rollback entity changes",
|
|
@@ -100,18 +100,18 @@ async function rollback(state, scheduleFetch, scheduleProcessing, scheduleRollba
|
|
|
100
100
|
}
|
|
101
101
|
switch (match.TAG) {
|
|
102
102
|
case "ReorgDetected" :
|
|
103
|
-
let
|
|
104
|
-
let chainState = IndexerState.getChainState(state,
|
|
103
|
+
let chainId = match.chainId;
|
|
104
|
+
let chainState = IndexerState.getChainState(state, chainId);
|
|
105
105
|
IndexerState.enterFindingReorgDepth(state);
|
|
106
106
|
let rollbackTargetBlockNumber = await getLastKnownValidBlock(chainState, match.blockNumber, IndexerState.isRealtime(state));
|
|
107
107
|
SourceManager.onReorg(ChainState.sourceManager(chainState), rollbackTargetBlockNumber);
|
|
108
|
-
IndexerState.foundReorgDepth(state,
|
|
108
|
+
IndexerState.foundReorgDepth(state, chainId, rollbackTargetBlockNumber);
|
|
109
109
|
return scheduleRollback();
|
|
110
110
|
case "FoundReorgDepth" :
|
|
111
111
|
if (IndexerState.isProcessing(state)) {
|
|
112
112
|
return Logging.trace("Waiting for batch to finish processing before executing rollback");
|
|
113
113
|
} else {
|
|
114
|
-
return await executeRollback(state, match.
|
|
114
|
+
return await executeRollback(state, match.chainId, match.rollbackTargetBlockNumber, scheduleFetch, scheduleProcessing);
|
|
115
115
|
}
|
|
116
116
|
case "RollbackReady" :
|
|
117
117
|
return Stdlib_JsError.throwWithMessage("Internal error: Rollback initiated with invalid state");
|
package/src/RollbackCommit.res
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// feature lives here plus two call sites: registration in `Main.res` and the
|
|
4
4
|
// fire on a successful rollback write in `InMemoryStore.res`. Delete those
|
|
5
5
|
// together with this module.
|
|
6
|
-
type args = {chainId:
|
|
6
|
+
type args = {chainId: ChainId.t, rollbackToBlock: int}
|
|
7
7
|
type callback = args => promise<unit>
|
|
8
8
|
|
|
9
9
|
// Lives in the process-wide `EnvioGlobal` record so callbacks registered
|
|
@@ -28,7 +28,7 @@ let fire = async (~progressBlockNumberByChainId: dict<int>) => {
|
|
|
28
28
|
let _ = await progressBlockNumberByChainId
|
|
29
29
|
->Dict.toArray
|
|
30
30
|
->Array.flatMap(((chainIdKey, rollbackToBlock)) => {
|
|
31
|
-
let args = {chainId: chainIdKey->
|
|
31
|
+
let args = {chainId: chainIdKey->ChainId.normalizeOrThrow, rollbackToBlock}
|
|
32
32
|
callbacks->Array.map(callback => callback(args))
|
|
33
33
|
})
|
|
34
34
|
->Promise.all
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as
|
|
3
|
+
import * as ChainId from "./ChainId.res.mjs";
|
|
4
4
|
import * as EnvioGlobal from "./EnvioGlobal.res.mjs";
|
|
5
5
|
|
|
6
6
|
let callbacks = EnvioGlobal.value.rollbackCommitCallbacks;
|
|
@@ -18,7 +18,7 @@ function register(callback) {
|
|
|
18
18
|
|
|
19
19
|
async function fire(progressBlockNumberByChainId) {
|
|
20
20
|
await Promise.all(Object.entries(progressBlockNumberByChainId).flatMap(param => {
|
|
21
|
-
let args_chainId =
|
|
21
|
+
let args_chainId = ChainId.normalizeOrThrow(param[0]);
|
|
22
22
|
let args_rollbackToBlock = param[1];
|
|
23
23
|
let args = {
|
|
24
24
|
chainId: args_chainId,
|
|
@@ -33,4 +33,4 @@ export {
|
|
|
33
33
|
register,
|
|
34
34
|
fire,
|
|
35
35
|
}
|
|
36
|
-
/*
|
|
36
|
+
/* ChainId Not a pure module */
|
|
@@ -70,10 +70,10 @@ let getSafeCheckpointId = (safeCheckpointTracking: t, ~sourceBlockNumber: int) =
|
|
|
70
70
|
let updateOnNewBatch = (
|
|
71
71
|
safeCheckpointTracking: t,
|
|
72
72
|
~sourceBlockNumber: int,
|
|
73
|
-
~chainId:
|
|
73
|
+
~chainId: ChainId.t,
|
|
74
74
|
~batchCheckpointIds: array<bigint>,
|
|
75
75
|
~batchCheckpointBlockNumbers: array<int>,
|
|
76
|
-
~batchCheckpointChainIds: array<
|
|
76
|
+
~batchCheckpointChainIds: array<ChainId.t>,
|
|
77
77
|
) => {
|
|
78
78
|
let safeCheckpointId = getSafeCheckpointId(safeCheckpointTracking, ~sourceBlockNumber)
|
|
79
79
|
|
|
@@ -6,16 +6,15 @@
|
|
|
6
6
|
// transform of the item between the source and the batch.
|
|
7
7
|
let itemKey = (item: Internal.item): string =>
|
|
8
8
|
switch item {
|
|
9
|
-
| Internal.Event({
|
|
10
|
-
`${
|
|
11
|
-
->
|
|
12
|
-
->Int.toString}:${blockNumber->Int.toString}:${logIndex->Int.toString}`
|
|
9
|
+
| Internal.Event({chainId, blockNumber, logIndex}) =>
|
|
10
|
+
`${chainId
|
|
11
|
+
->ChainId.toString}:${blockNumber->Int.toString}:${logIndex->Int.toString}`
|
|
13
12
|
| Internal.Block(_) => ""
|
|
14
13
|
}
|
|
15
14
|
|
|
16
15
|
// `index` is the item's position in its chain's `simulate` array, reported back
|
|
17
16
|
// so a user finds it without echoing its fields.
|
|
18
|
-
type entry = {chainId:
|
|
17
|
+
type entry = {chainId: ChainId.t, index: int, key: string}
|
|
19
18
|
|
|
20
19
|
type t = {mutable unprocessed: array<entry>}
|
|
21
20
|
|
|
@@ -25,18 +24,12 @@ let makeFromConfig = (config: Config.t): option<t> => {
|
|
|
25
24
|
->ChainMap.values
|
|
26
25
|
->Array.flatMap(chainConfig =>
|
|
27
26
|
switch chainConfig.sourceConfig {
|
|
28
|
-
| Config.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
chainId: chainConfig.id,
|
|
35
|
-
index,
|
|
36
|
-
key: itemKey(item),
|
|
37
|
-
},
|
|
38
|
-
)
|
|
39
|
-
)
|
|
27
|
+
| Config.SimulateSourceConfig({items}) =>
|
|
28
|
+
items->Array.mapWithIndex((item, index) => {
|
|
29
|
+
chainId: chainConfig.id,
|
|
30
|
+
index,
|
|
31
|
+
key: itemKey(item),
|
|
32
|
+
})
|
|
40
33
|
| _ => []
|
|
41
34
|
}
|
|
42
35
|
)
|
|
@@ -52,17 +45,20 @@ let recordProcessed = (t: t, ~batch: Batch.t) => {
|
|
|
52
45
|
}
|
|
53
46
|
|
|
54
47
|
// Unrouted item indices grouped by chain, in the order chains were first seen.
|
|
55
|
-
let unroutedByChain = (t: t): array<(
|
|
48
|
+
let unroutedByChain = (t: t): array<(ChainId.t, array<int>)> => {
|
|
56
49
|
let indicesByChain = Dict.make()
|
|
57
50
|
let chainOrder = []
|
|
58
51
|
t.unprocessed->Array.forEach(entry => {
|
|
59
|
-
let key = entry.chainId->
|
|
52
|
+
let key = entry.chainId->ChainId.toString
|
|
60
53
|
if indicesByChain->Dict.get(key)->Option.isNone {
|
|
61
54
|
chainOrder->Array.push(entry.chainId)->ignore
|
|
62
55
|
}
|
|
63
56
|
indicesByChain->Utils.Dict.push(key, entry.index)
|
|
64
57
|
})
|
|
65
|
-
chainOrder->Array.map(chainId => (
|
|
58
|
+
chainOrder->Array.map(chainId => (
|
|
59
|
+
chainId,
|
|
60
|
+
indicesByChain->Dict.getUnsafe(chainId->ChainId.toString),
|
|
61
|
+
))
|
|
66
62
|
}
|
|
67
63
|
|
|
68
64
|
let failureMessage = (t: t): option<string> =>
|
|
@@ -74,7 +70,7 @@ let failureMessage = (t: t): option<string> =>
|
|
|
74
70
|
let lines =
|
|
75
71
|
byChain
|
|
76
72
|
->Array.map(((chainId, indices)) =>
|
|
77
|
-
` - chain ${chainId->
|
|
73
|
+
` - chain ${chainId->ChainId.toString}: ${indices
|
|
78
74
|
->Array.map(index => index->Int.toString)
|
|
79
75
|
->Array.join(", ")}`
|
|
80
76
|
)
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Utils from "./Utils.res.mjs";
|
|
4
|
+
import * as ChainId from "./ChainId.res.mjs";
|
|
4
5
|
import * as ChainMap from "./ChainMap.res.mjs";
|
|
5
6
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
6
7
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
7
8
|
|
|
8
9
|
function itemKey(item) {
|
|
9
10
|
if (item.kind === 0) {
|
|
10
|
-
return
|
|
11
|
+
return ChainId.toString(item.chainId) + `:` + item.blockNumber.toString() + `:` + item.logIndex.toString();
|
|
11
12
|
} else {
|
|
12
13
|
return "";
|
|
13
14
|
}
|
|
@@ -15,13 +16,13 @@ function itemKey(item) {
|
|
|
15
16
|
|
|
16
17
|
function makeFromConfig(config) {
|
|
17
18
|
let entries = ChainMap.values(config.chainMap).flatMap(chainConfig => {
|
|
18
|
-
let
|
|
19
|
-
if (
|
|
20
|
-
return
|
|
19
|
+
let match = chainConfig.sourceConfig;
|
|
20
|
+
if (match.TAG === "SimulateSourceConfig") {
|
|
21
|
+
return match.items.map((item, index) => ({
|
|
21
22
|
chainId: chainConfig.id,
|
|
22
23
|
index: index,
|
|
23
24
|
key: itemKey(item)
|
|
24
|
-
}))
|
|
25
|
+
}));
|
|
25
26
|
} else {
|
|
26
27
|
return [];
|
|
27
28
|
}
|
|
@@ -42,7 +43,7 @@ function unroutedByChain(t) {
|
|
|
42
43
|
let indicesByChain = {};
|
|
43
44
|
let chainOrder = [];
|
|
44
45
|
t.unprocessed.forEach(entry => {
|
|
45
|
-
let key = entry.chainId
|
|
46
|
+
let key = ChainId.toString(entry.chainId);
|
|
46
47
|
if (Stdlib_Option.isNone(indicesByChain[key])) {
|
|
47
48
|
chainOrder.push(entry.chainId);
|
|
48
49
|
}
|
|
@@ -50,7 +51,7 @@ function unroutedByChain(t) {
|
|
|
50
51
|
});
|
|
51
52
|
return chainOrder.map(chainId => [
|
|
52
53
|
chainId,
|
|
53
|
-
indicesByChain[
|
|
54
|
+
indicesByChain[ChainId.toString(chainId)]
|
|
54
55
|
]);
|
|
55
56
|
}
|
|
56
57
|
|
|
@@ -61,7 +62,7 @@ function failureMessage(t) {
|
|
|
61
62
|
}
|
|
62
63
|
let count = Stdlib_Array.reduce(byChain, 0, (acc, param) => acc + param[1].length | 0);
|
|
63
64
|
let itemWord = count === 1 ? "item" : "items";
|
|
64
|
-
let lines = byChain.map(param => ` - chain ` + param[0]
|
|
65
|
+
let lines = byChain.map(param => ` - chain ` + ChainId.toString(param[0]) + `: ` + param[1].map(index => index.toString()).join(", ")).join("\n");
|
|
65
66
|
return `simulate: ` + count.toString() + ` ` + itemWord + ` you passed to simulate never reached a handler, so nothing ran for them. Each was filtered out before the handler — usually a non-wildcard srcAddress that isn't indexed for the contract, or a where/block filter that excluded the event. Unrouted items, by index in each chain's simulate array:\n` + lines;
|
|
66
67
|
}
|
|
67
68
|
|
package/src/SimulateItems.res
CHANGED
|
@@ -246,7 +246,6 @@ let parse = (
|
|
|
246
246
|
~chainConfig: Config.chain,
|
|
247
247
|
~onEventRegistrations: array<Internal.onEventRegistration>,
|
|
248
248
|
): array<Internal.item> => {
|
|
249
|
-
let chain = ChainMap.Chain.makeUnsafe(~chainId=chainConfig.id)
|
|
250
249
|
let chainId = chainConfig.id
|
|
251
250
|
let startBlock = chainConfig.startBlock
|
|
252
251
|
let currentBlock = ref(startBlock)
|
|
@@ -334,7 +333,7 @@ let parse = (
|
|
|
334
333
|
switch seenCoordinates->Dict.get(coordinate) {
|
|
335
334
|
| Some(firstIndex) =>
|
|
336
335
|
JsError.throwWithMessage(
|
|
337
|
-
`simulate: items at index ${firstIndex->Int.toString} and ${itemIndex->Int.toString} on chain ${chainId->
|
|
336
|
+
`simulate: items at index ${firstIndex->Int.toString} and ${itemIndex->Int.toString} on chain ${chainId->ChainId.toString} both resolve to block ${blockNumber->Int.toString}, logIndex ${logIndex->Int.toString}. Give each item a distinct logIndex (or omit logIndex so they auto-increment).`,
|
|
338
337
|
)
|
|
339
338
|
| None => seenCoordinates->Dict.set(coordinate, itemIndex)
|
|
340
339
|
}
|
|
@@ -351,7 +350,7 @@ let parse = (
|
|
|
351
350
|
let liveRegistrations =
|
|
352
351
|
HandlerRegister.getSimulateOnEventRegistrations(
|
|
353
352
|
~config,
|
|
354
|
-
~chainId,
|
|
353
|
+
~chainId=chainId,
|
|
355
354
|
~eventConfig,
|
|
356
355
|
)->Array.filter(reg =>
|
|
357
356
|
(reg.handler->Option.isSome || reg.contractRegister->Option.isSome) &&
|
|
@@ -363,7 +362,7 @@ let parse = (
|
|
|
363
362
|
->ChainMap.values
|
|
364
363
|
->Array.length {
|
|
365
364
|
| 1 => ""
|
|
366
|
-
| _ => ` on chain ${chainId->
|
|
365
|
+
| _ => ` on chain ${chainId->ChainId.toString}`
|
|
367
366
|
}}. Register a handler with indexer.onEvent (and check any \`where\` filter isn't excluding this chain) before simulating it.`,
|
|
368
367
|
)
|
|
369
368
|
}
|
|
@@ -378,7 +377,7 @@ let parse = (
|
|
|
378
377
|
->Array.push(
|
|
379
378
|
Internal.Event({
|
|
380
379
|
onEventRegistration,
|
|
381
|
-
|
|
380
|
+
chainId,
|
|
382
381
|
blockNumber,
|
|
383
382
|
logIndex,
|
|
384
383
|
// Simulate keeps the transaction inline on the payload, so the store
|
|
@@ -389,7 +388,7 @@ let parse = (
|
|
|
389
388
|
contractName: eventConfig.contractName,
|
|
390
389
|
eventName: eventConfig.name,
|
|
391
390
|
params,
|
|
392
|
-
chainId,
|
|
391
|
+
chainId: chainId,
|
|
393
392
|
srcAddress,
|
|
394
393
|
logIndex,
|
|
395
394
|
transaction,
|
|
@@ -420,8 +419,8 @@ let patchConfig = (
|
|
|
420
419
|
(processConfig->(Utils.magic: JSON.t => {..}))["chains"]->Nullable.toOption
|
|
421
420
|
switch processChains {
|
|
422
421
|
| Some(chainsDict) =>
|
|
423
|
-
let newChainMap = config.chainMap->ChainMap.mapWithKey((
|
|
424
|
-
let chainIdStr =
|
|
422
|
+
let newChainMap = config.chainMap->ChainMap.mapWithKey((chainId, chainConfig) => {
|
|
423
|
+
let chainIdStr = chainId->ChainId.toString
|
|
425
424
|
switch chainsDict->Dict.get(chainIdStr) {
|
|
426
425
|
| Some(processChainJson) =>
|
|
427
426
|
let raw = processChainJson->(Utils.magic: JSON.t => {..})
|
|
@@ -451,8 +450,7 @@ let patchConfig = (
|
|
|
451
450
|
~chainConfig,
|
|
452
451
|
~onEventRegistrations=chainRegistrations.onEventRegistrations,
|
|
453
452
|
)
|
|
454
|
-
|
|
455
|
-
{...chainConfig, sourceConfig: Config.CustomSources([source])}
|
|
453
|
+
{...chainConfig, sourceConfig: Config.SimulateSourceConfig({items, endBlock})}
|
|
456
454
|
| None => chainConfig
|
|
457
455
|
}
|
|
458
456
|
| None => chainConfig
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
import * as Utils from "./Utils.res.mjs";
|
|
4
4
|
import * as Config from "./Config.res.mjs";
|
|
5
5
|
import * as Address from "./Address.res.mjs";
|
|
6
|
+
import * as ChainId from "./ChainId.res.mjs";
|
|
6
7
|
import * as ChainMap from "./ChainMap.res.mjs";
|
|
7
8
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
8
|
-
import * as SimulateSource from "./sources/SimulateSource.res.mjs";
|
|
9
9
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
10
10
|
import * as HandlerRegister from "./HandlerRegister.res.mjs";
|
|
11
11
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
@@ -169,7 +169,6 @@ function deriveSrcAddress(providedSrcAddress, eventConfig, chainConfig, config)
|
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
function parse(simulateItems, config, chainConfig, onEventRegistrations) {
|
|
172
|
-
let chain = ChainMap.Chain.makeUnsafe(chainConfig.id);
|
|
173
172
|
let chainId = chainConfig.id;
|
|
174
173
|
let startBlock = chainConfig.startBlock;
|
|
175
174
|
let currentBlock = {
|
|
@@ -251,7 +250,7 @@ function parse(simulateItems, config, chainConfig, onEventRegistrations) {
|
|
|
251
250
|
let coordinate = blockNumber.toString() + `:` + logIndex.toString();
|
|
252
251
|
let firstIndex = seenCoordinates[coordinate];
|
|
253
252
|
if (firstIndex !== undefined) {
|
|
254
|
-
Stdlib_JsError.throwWithMessage(`simulate: items at index ` + firstIndex.toString() + ` and ` + itemIndex.toString() + ` on chain ` +
|
|
253
|
+
Stdlib_JsError.throwWithMessage(`simulate: items at index ` + firstIndex.toString() + ` and ` + itemIndex.toString() + ` on chain ` + ChainId.toString(chainId) + ` both resolve to block ` + blockNumber.toString() + `, logIndex ` + logIndex.toString() + `. Give each item a distinct logIndex (or omit logIndex so they auto-increment).`);
|
|
255
254
|
} else {
|
|
256
255
|
seenCoordinates[coordinate] = itemIndex;
|
|
257
256
|
}
|
|
@@ -265,7 +264,7 @@ function parse(simulateItems, config, chainConfig, onEventRegistrations) {
|
|
|
265
264
|
if (Utils.$$Array.isEmpty(liveRegistrations)) {
|
|
266
265
|
let match$5 = ChainMap.values(config.chainMap).length;
|
|
267
266
|
Stdlib_JsError.throwWithMessage(`simulate: no handler runs for event "` + match$1 + `" on contract "` + match + `"` + (
|
|
268
|
-
match$5 !== 1 ? ` on chain ` +
|
|
267
|
+
match$5 !== 1 ? ` on chain ` + ChainId.toString(chainId) : ""
|
|
269
268
|
) + `. Register a handler with indexer.onEvent (and check any \`where\` filter isn't excluding this chain) before simulating it.`);
|
|
270
269
|
}
|
|
271
270
|
liveRegistrations.forEach(reg => {
|
|
@@ -276,7 +275,7 @@ function parse(simulateItems, config, chainConfig, onEventRegistrations) {
|
|
|
276
275
|
items.push({
|
|
277
276
|
kind: 0,
|
|
278
277
|
onEventRegistration: newrecord,
|
|
279
|
-
|
|
278
|
+
chainId: chainId,
|
|
280
279
|
blockNumber: blockNumber,
|
|
281
280
|
logIndex: logIndex,
|
|
282
281
|
transactionIndex: 0,
|
|
@@ -301,8 +300,8 @@ function patchConfig(config, processConfig, registrationsByChainId) {
|
|
|
301
300
|
if (processChains == null) {
|
|
302
301
|
return config;
|
|
303
302
|
}
|
|
304
|
-
let newChainMap = ChainMap.mapWithKey(config.chainMap, (
|
|
305
|
-
let chainIdStr =
|
|
303
|
+
let newChainMap = ChainMap.mapWithKey(config.chainMap, (chainId, chainConfig) => {
|
|
304
|
+
let chainIdStr = ChainId.toString(chainId);
|
|
306
305
|
let processChainJson = processChains[chainIdStr];
|
|
307
306
|
if (processChainJson === undefined) {
|
|
308
307
|
return chainConfig;
|
|
@@ -331,11 +330,11 @@ function patchConfig(config, processConfig, registrationsByChainId) {
|
|
|
331
330
|
newrecord.endBlock = endBlock;
|
|
332
331
|
newrecord.startBlock = startBlock;
|
|
333
332
|
let items = parse(simulateRaw, config, newrecord, chainRegistrations.onEventRegistrations);
|
|
334
|
-
let source = SimulateSource.make(items, endBlock, chain);
|
|
335
333
|
let newrecord$1 = {...newrecord};
|
|
336
334
|
newrecord$1.sourceConfig = {
|
|
337
|
-
TAG: "
|
|
338
|
-
|
|
335
|
+
TAG: "SimulateSourceConfig",
|
|
336
|
+
items: items,
|
|
337
|
+
endBlock: endBlock
|
|
339
338
|
};
|
|
340
339
|
return newrecord$1;
|
|
341
340
|
});
|
package/src/Sink.res
CHANGED
|
@@ -12,7 +12,7 @@ type t = {
|
|
|
12
12
|
) => promise<unit>,
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
let makeClickHouse = (~host, ~database, ~username, ~password): t => {
|
|
15
|
+
let makeClickHouse = (~host, ~database, ~username, ~password, ~chainIdMode: ChainId.mode=Int32): t => {
|
|
16
16
|
let client = ClickHouse.createClient({
|
|
17
17
|
url: host,
|
|
18
18
|
username,
|
|
@@ -27,7 +27,7 @@ let makeClickHouse = (~host, ~database, ~username, ~password): t => {
|
|
|
27
27
|
{
|
|
28
28
|
name: "clickhouse",
|
|
29
29
|
initialize: (~chainConfigs as _=[], ~entities=[], ~enums=[]) => {
|
|
30
|
-
ClickHouse.initialize(client, ~database, ~entities, ~enums)
|
|
30
|
+
ClickHouse.initialize(client, ~database, ~entities, ~enums, ~chainIdMode)
|
|
31
31
|
},
|
|
32
32
|
resume: (~checkpointId) => {
|
|
33
33
|
ClickHouse.resume(client, ~database, ~checkpointId)
|
package/src/Sink.res.mjs
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
import * as ClickHouse from "./bindings/ClickHouse.res.mjs";
|
|
4
4
|
import * as Client from "@clickhouse/client";
|
|
5
5
|
|
|
6
|
-
function makeClickHouse(host, database, username, password) {
|
|
6
|
+
function makeClickHouse(host, database, username, password, chainIdModeOpt) {
|
|
7
|
+
let chainIdMode = chainIdModeOpt !== undefined ? chainIdModeOpt : "int32";
|
|
7
8
|
let client = Client.createClient({
|
|
8
9
|
url: host,
|
|
9
10
|
username: username,
|
|
@@ -16,7 +17,7 @@ function makeClickHouse(host, database, username, password) {
|
|
|
16
17
|
$staropt$star !== undefined;
|
|
17
18
|
let entities = $staropt$star$1 !== undefined ? $staropt$star$1 : [];
|
|
18
19
|
let enums = $staropt$star$2 !== undefined ? $staropt$star$2 : [];
|
|
19
|
-
return ClickHouse.initialize(client, database, entities, enums);
|
|
20
|
+
return ClickHouse.initialize(client, database, entities, enums, chainIdMode);
|
|
20
21
|
},
|
|
21
22
|
resume: checkpointId => ClickHouse.resume(client, database, checkpointId),
|
|
22
23
|
writeBatch: async (batch, updatedEntities) => {
|
package/src/TestIndexer.res
CHANGED
|
@@ -55,7 +55,7 @@ let getIndexingAddressesByChain = (state: testIndexerState): dict<
|
|
|
55
55
|
->Dict.valuesToArray
|
|
56
56
|
->Array.forEach(entity => {
|
|
57
57
|
let dc = entity->castToEnvioAddresses
|
|
58
|
-
let chainIdStr = dc.chainId->
|
|
58
|
+
let chainIdStr = dc.chainId->ChainId.toString
|
|
59
59
|
let contracts = switch byChain->Dict.get(chainIdStr) {
|
|
60
60
|
| Some(arr) => arr
|
|
61
61
|
| None =>
|
|
@@ -96,7 +96,7 @@ let handleWriteBatch = (
|
|
|
96
96
|
state: testIndexerState,
|
|
97
97
|
~updatedEntities: array<Persistence.updatedEntity>,
|
|
98
98
|
~checkpointIds: array<bigint>,
|
|
99
|
-
~checkpointChainIds: array<
|
|
99
|
+
~checkpointChainIds: array<ChainId.t>,
|
|
100
100
|
~checkpointBlockNumbers: array<int>,
|
|
101
101
|
~checkpointEventsProcessed: array<int>,
|
|
102
102
|
): unit => {
|
|
@@ -158,7 +158,7 @@ let handleWriteBatch = (
|
|
|
158
158
|
|
|
159
159
|
// Update progress tracking from checkpoint data
|
|
160
160
|
state.progressBlockByChain->Dict.set(
|
|
161
|
-
checkpointChainIds->Array.getUnsafe(i)->
|
|
161
|
+
checkpointChainIds->Array.getUnsafe(i)->ChainId.toString,
|
|
162
162
|
checkpointBlockNumbers->Array.getUnsafe(i),
|
|
163
163
|
)
|
|
164
164
|
|
|
@@ -223,8 +223,7 @@ let makeInitialState = (
|
|
|
223
223
|
): Persistence.initialState => {
|
|
224
224
|
let chainKeys = processConfigChains->Dict.keysToArray
|
|
225
225
|
let chains = chainKeys->Array.map(chainIdStr => {
|
|
226
|
-
let
|
|
227
|
-
let chain = ChainMap.Chain.makeUnsafe(~chainId)
|
|
226
|
+
let chain = chainIdStr->ChainId.normalizeOrThrow
|
|
228
227
|
|
|
229
228
|
if !(config.chainMap->ChainMap.has(chain)) {
|
|
230
229
|
JsError.throwWithMessage(`Chain ${chainIdStr} is not configured in config.yaml`)
|
|
@@ -233,7 +232,7 @@ let makeInitialState = (
|
|
|
233
232
|
let processChainConfig = processConfigChains->Dict.getUnsafe(chainIdStr)
|
|
234
233
|
let indexingAddresses = indexingAddressesByChain->Dict.get(chainIdStr)->Option.getOr([])
|
|
235
234
|
{
|
|
236
|
-
Persistence.id:
|
|
235
|
+
Persistence.id: chain,
|
|
237
236
|
startBlock: processChainConfig.startBlock,
|
|
238
237
|
endBlock: processChainConfig.endBlock,
|
|
239
238
|
sourceBlockNumber: processChainConfig.endBlock->Option.getOr(0),
|
|
@@ -310,12 +309,9 @@ let parseBlockRange = (
|
|
|
310
309
|
~rawChainConfig: rawChainConfig,
|
|
311
310
|
~progressBlock: option<int>,
|
|
312
311
|
): chainConfig => {
|
|
313
|
-
let
|
|
314
|
-
|
|
|
315
|
-
| None =>
|
|
316
|
-
JsError.throwWithMessage(`Invalid chain ID "${chainIdStr}": expected a numeric chain ID`)
|
|
312
|
+
let chain = try chainIdStr->ChainId.normalizeOrThrow catch {
|
|
313
|
+
| _ => JsError.throwWithMessage(`Invalid chain ID "${chainIdStr}": expected a numeric chain ID`)
|
|
317
314
|
}
|
|
318
|
-
let chain = ChainMap.Chain.makeUnsafe(~chainId)
|
|
319
315
|
if !(config.chainMap->ChainMap.has(chain)) {
|
|
320
316
|
JsError.throwWithMessage(`Chain ${chainIdStr} is not configured in config.yaml`)
|
|
321
317
|
}
|
|
@@ -485,6 +481,10 @@ let makeInMemoryStorage = (~state: testIndexerState): Persistence.storage => {
|
|
|
485
481
|
state
|
|
486
482
|
->handleLoad(~tableName=table.tableName, ~filter)
|
|
487
483
|
->(Utils.magic: array<Internal.entity> => array<unknown>),
|
|
484
|
+
// The in-memory storage has no indexes to build, and it's always ready.
|
|
485
|
+
ensureQueryIndexes: async (~table as _, ~filters as _) => (),
|
|
486
|
+
ensureSchemaIndexes: async (~entities as _) => (),
|
|
487
|
+
finalizeBackfill: async (~entities as _, ~chainIds as _, ~readyAt as _) => (),
|
|
488
488
|
writeBatch: async (
|
|
489
489
|
~batch,
|
|
490
490
|
~rollback as _,
|
|
@@ -638,7 +638,7 @@ let createTestIndexer = (): t<'processConfig> => {
|
|
|
638
638
|
config.chainMap
|
|
639
639
|
->ChainMap.values
|
|
640
640
|
->Array.forEach(chainConfig => {
|
|
641
|
-
let chainIdStr = chainConfig.id->
|
|
641
|
+
let chainIdStr = chainConfig.id->ChainId.toString
|
|
642
642
|
chainIds->Array.push(chainConfig.id)->ignore
|
|
643
643
|
|
|
644
644
|
let chainObj = Utils.Object.createNullObject()
|
|
@@ -673,7 +673,7 @@ let createTestIndexer = (): t<'processConfig> => {
|
|
|
673
673
|
)
|
|
674
674
|
}
|
|
675
675
|
getIndexingAddressesByChain(state)
|
|
676
|
-
->Dict.get(chainConfig.id->
|
|
676
|
+
->Dict.get(chainConfig.id->ChainId.toString)
|
|
677
677
|
->Option.getOr([])
|
|
678
678
|
->Array.filterMap(ia => ia.contractName === contract.name ? Some(ia.address) : None)
|
|
679
679
|
},
|
|
@@ -699,7 +699,7 @@ let createTestIndexer = (): t<'processConfig> => {
|
|
|
699
699
|
|
|
700
700
|
// Build the result object with process + entity operations + chain info
|
|
701
701
|
let result: dict<unknown> = Dict.make()
|
|
702
|
-
result->Dict.set("chainIds", chainIds->(Utils.magic: array<
|
|
702
|
+
result->Dict.set("chainIds", chainIds->(Utils.magic: array<ChainId.t> => unknown))
|
|
703
703
|
result->Dict.set("chains", chains->(Utils.magic: {..} => unknown))
|
|
704
704
|
entityOpsDict
|
|
705
705
|
->Dict.toArray
|