envio 3.1.2 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/evm.schema.json +83 -11
- package/fuel.schema.json +83 -11
- package/index.d.ts +184 -3
- package/package.json +6 -6
- package/src/Batch.res +2 -2
- package/src/ChainFetcher.res +27 -3
- package/src/ChainFetcher.res.mjs +17 -3
- package/src/ChainManager.res +163 -0
- package/src/ChainManager.res.mjs +136 -0
- package/src/Config.res +213 -30
- package/src/Config.res.mjs +102 -41
- package/src/Core.res +16 -10
- package/src/Ecosystem.res +0 -3
- package/src/Env.res +2 -2
- package/src/Env.res.mjs +2 -2
- package/src/Envio.res +101 -2
- package/src/Envio.res.mjs +2 -3
- package/src/EventConfigBuilder.res +87 -0
- package/src/EventConfigBuilder.res.mjs +53 -0
- package/src/EventUtils.res +2 -2
- package/src/FetchState.res +63 -67
- package/src/FetchState.res.mjs +44 -42
- package/src/GlobalState.res +219 -363
- package/src/GlobalState.res.mjs +314 -491
- package/src/GlobalStateManager.res +49 -59
- package/src/GlobalStateManager.res.mjs +5 -4
- package/src/GlobalStateManager.resi +1 -1
- package/src/HandlerLoader.res +18 -2
- package/src/HandlerLoader.res.mjs +16 -34
- package/src/HandlerRegister.res +9 -9
- package/src/HandlerRegister.res.mjs +9 -9
- package/src/Hasura.res +102 -32
- package/src/Hasura.res.mjs +88 -34
- package/src/InMemoryStore.res +10 -1
- package/src/InMemoryStore.res.mjs +4 -1
- package/src/InMemoryTable.res +83 -136
- package/src/InMemoryTable.res.mjs +57 -86
- package/src/Internal.res +70 -5
- package/src/Internal.res.mjs +2 -8
- package/src/LazyLoader.res +2 -2
- package/src/LazyLoader.res.mjs +3 -3
- package/src/LoadLayer.res +47 -60
- package/src/LoadLayer.res.mjs +28 -50
- package/src/LoadLayer.resi +2 -5
- package/src/LogSelection.res +90 -21
- package/src/LogSelection.res.mjs +72 -21
- package/src/Logging.res +1 -1
- package/src/Main.res +61 -2
- package/src/Main.res.mjs +37 -1
- package/src/Persistence.res +3 -16
- package/src/PgStorage.res +125 -114
- package/src/PgStorage.res.mjs +112 -95
- package/src/Ports.res +5 -0
- package/src/Ports.res.mjs +9 -0
- package/src/Prometheus.res +3 -3
- package/src/Prometheus.res.mjs +4 -4
- package/src/ReorgDetection.res +4 -4
- package/src/ReorgDetection.res.mjs +4 -5
- package/src/SafeCheckpointTracking.res +16 -16
- package/src/SafeCheckpointTracking.res.mjs +2 -2
- package/src/SimulateItems.res +10 -14
- package/src/SimulateItems.res.mjs +5 -2
- package/src/Sink.res +1 -1
- package/src/Sink.res.mjs +1 -2
- package/src/SvmTypes.res +9 -0
- package/src/SvmTypes.res.mjs +14 -0
- package/src/TestIndexer.res +35 -68
- package/src/TestIndexer.res.mjs +17 -48
- package/src/TestIndexerProxyStorage.res +23 -23
- package/src/TestIndexerProxyStorage.res.mjs +12 -15
- package/src/Throttler.res +2 -2
- package/src/Time.res +2 -2
- package/src/Time.res.mjs +2 -2
- package/src/UserContext.res +19 -118
- package/src/UserContext.res.mjs +10 -66
- package/src/Utils.res +15 -15
- package/src/Utils.res.mjs +7 -8
- package/src/adapters/MarkBatchProcessedAdapter.res +5 -0
- package/src/adapters/MarkBatchProcessedAdapter.res.mjs +14 -0
- package/src/bindings/BigDecimal.res +1 -1
- package/src/bindings/BigDecimal.res.mjs +2 -2
- package/src/bindings/ClickHouse.res +8 -6
- package/src/bindings/ClickHouse.res.mjs +5 -5
- package/src/bindings/Hrtime.res +1 -1
- package/src/bindings/Pino.res +2 -2
- package/src/bindings/Pino.res.mjs +3 -4
- package/src/db/EntityFilter.res +410 -0
- package/src/db/EntityFilter.res.mjs +424 -0
- package/src/db/EntityHistory.res +1 -1
- package/src/db/EntityHistory.res.mjs +1 -1
- package/src/db/InternalTable.res +10 -10
- package/src/db/InternalTable.res.mjs +41 -45
- package/src/db/Schema.res +2 -2
- package/src/db/Schema.res.mjs +3 -3
- package/src/db/Table.res +106 -22
- package/src/db/Table.res.mjs +84 -35
- package/src/sources/EventRouter.res +67 -2
- package/src/sources/EventRouter.res.mjs +45 -3
- package/src/sources/Evm.res +0 -7
- package/src/sources/Evm.res.mjs +0 -15
- package/src/sources/EvmChain.res +1 -1
- package/src/sources/EvmChain.res.mjs +1 -2
- package/src/sources/EvmRpcClient.res +42 -0
- package/src/sources/EvmRpcClient.res.mjs +64 -0
- package/src/sources/Fuel.res +0 -7
- package/src/sources/Fuel.res.mjs +0 -15
- package/src/sources/HyperFuelSource.res +5 -4
- package/src/sources/HyperFuelSource.res.mjs +2 -2
- package/src/sources/HyperSyncClient.res +9 -5
- package/src/sources/HyperSyncClient.res.mjs +2 -2
- package/src/sources/HyperSyncHeightStream.res +2 -2
- package/src/sources/HyperSyncHeightStream.res.mjs +2 -2
- package/src/sources/HyperSyncSource.res +12 -11
- package/src/sources/HyperSyncSource.res.mjs +6 -6
- package/src/sources/Rpc.res +1 -5
- package/src/sources/Rpc.res.mjs +1 -9
- package/src/sources/RpcSource.res +57 -21
- package/src/sources/RpcSource.res.mjs +47 -20
- package/src/sources/RpcWebSocketHeightStream.res +1 -1
- package/src/sources/SourceManager.res +3 -2
- package/src/sources/SourceManager.res.mjs +1 -1
- package/src/sources/Svm.res +3 -10
- package/src/sources/Svm.res.mjs +4 -18
- package/src/sources/SvmHyperSyncClient.res +265 -0
- package/src/sources/SvmHyperSyncClient.res.mjs +28 -0
- package/src/sources/SvmHyperSyncSource.res +638 -0
- package/src/sources/SvmHyperSyncSource.res.mjs +557 -0
- package/src/tui/Tui.res +9 -2
- package/src/tui/Tui.res.mjs +18 -3
- package/src/tui/components/BufferedProgressBar.res +2 -2
- package/src/tui/components/TuiData.res +3 -0
- package/svm.schema.json +523 -14
- package/src/TableIndices.res +0 -115
- package/src/TableIndices.res.mjs +0 -144
package/src/LoadLayer.res.mjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
+
import * as Table from "./db/Table.res.mjs";
|
|
3
4
|
import * as Utils from "./Utils.res.mjs";
|
|
4
5
|
import * as Hrtime from "./bindings/Hrtime.res.mjs";
|
|
5
6
|
import * as Logging from "./Logging.res.mjs";
|
|
6
|
-
import * as Internal from "./Internal.res.mjs";
|
|
7
|
-
import * as Belt_Array from "@rescript/runtime/lib/es6/Belt_Array.js";
|
|
8
7
|
import * as Prometheus from "./Prometheus.res.mjs";
|
|
9
8
|
import * as LoadManager from "./LoadManager.res.mjs";
|
|
10
9
|
import * as Persistence from "./Persistence.res.mjs";
|
|
11
|
-
import * as
|
|
10
|
+
import * as EntityFilter from "./db/EntityFilter.res.mjs";
|
|
11
|
+
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
12
12
|
import * as ErrorHandling from "./ErrorHandling.res.mjs";
|
|
13
13
|
import * as InMemoryStore from "./InMemoryStore.res.mjs";
|
|
14
14
|
import * as InMemoryTable from "./InMemoryTable.res.mjs";
|
|
@@ -23,7 +23,11 @@ function loadById(loadManager, persistence, entityConfig, inMemoryStore, shouldG
|
|
|
23
23
|
let timerRef = Prometheus.StorageLoad.startOperation(storage.name, key);
|
|
24
24
|
let dbEntities;
|
|
25
25
|
try {
|
|
26
|
-
dbEntities = await storage.
|
|
26
|
+
dbEntities = await storage.loadOrThrow({
|
|
27
|
+
operator: "in",
|
|
28
|
+
fieldName: Table.idFieldName,
|
|
29
|
+
fieldValue: idsToLoad
|
|
30
|
+
}, entityConfig.table);
|
|
27
31
|
} catch (raw_exn) {
|
|
28
32
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
29
33
|
if (exn.RE_EXN_ID === Persistence.StorageError) {
|
|
@@ -79,8 +83,8 @@ function executeWithRateLimit(effect, effectArgs, inMemTable, onError, isFromQue
|
|
|
79
83
|
state.nextWindowPromise = undefined;
|
|
80
84
|
}
|
|
81
85
|
let immediateCount = Math.min(state.availableCalls, effectArgs.length);
|
|
82
|
-
let immediateArgs =
|
|
83
|
-
let queuedArgs =
|
|
86
|
+
let immediateArgs = effectArgs.slice(0, immediateCount);
|
|
87
|
+
let queuedArgs = effectArgs.slice(immediateCount);
|
|
84
88
|
state.availableCalls = state.availableCalls - immediateCount | 0;
|
|
85
89
|
for (let idx = 0, idx_finish = immediateArgs.length; idx < idx_finish; ++idx) {
|
|
86
90
|
promises.push(callEffect(effect, immediateArgs[idx], inMemTable, timerRef, onError));
|
|
@@ -139,7 +143,11 @@ function loadEffect(loadManager, persistence, effect, effectArgs, inMemoryStore,
|
|
|
139
143
|
let outputSchema = match$1.outputSchema;
|
|
140
144
|
let dbEntities;
|
|
141
145
|
try {
|
|
142
|
-
dbEntities = await storage.
|
|
146
|
+
dbEntities = await storage.loadOrThrow({
|
|
147
|
+
operator: "in",
|
|
148
|
+
fieldName: Table.idFieldName,
|
|
149
|
+
fieldValue: idsToLoad
|
|
150
|
+
}, match$1.table);
|
|
143
151
|
} catch (raw_exn) {
|
|
144
152
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
145
153
|
Logging.childWarn(Logging.getItemLogger(item), {
|
|
@@ -189,48 +197,20 @@ function loadEffect(loadManager, persistence, effect, effectArgs, inMemoryStore,
|
|
|
189
197
|
return LoadManager.call(loadManager, effectArgs, key, load, args => args.cacheKey, shouldGroup, hash => InMemoryStore.hasEffectOutput(inMemTable, hash), hash => InMemoryStore.getEffectOutputUnsafe(inMemTable, hash));
|
|
190
198
|
}
|
|
191
199
|
|
|
192
|
-
function
|
|
193
|
-
let
|
|
194
|
-
switch (operator) {
|
|
195
|
-
case "Eq" :
|
|
196
|
-
operatorCallName = "eq";
|
|
197
|
-
break;
|
|
198
|
-
case "Gt" :
|
|
199
|
-
operatorCallName = "gt";
|
|
200
|
-
break;
|
|
201
|
-
case "Lt" :
|
|
202
|
-
operatorCallName = "lt";
|
|
203
|
-
break;
|
|
204
|
-
}
|
|
205
|
-
let key = entityConfig.name + `.getWhere.` + fieldName + `.` + operatorCallName;
|
|
200
|
+
function loadByFilter(loadManager, persistence, entityConfig, inMemoryStore, shouldGroup, item, filter) {
|
|
201
|
+
let key = EntityFilter.toOperationKey(filter, entityConfig.name);
|
|
206
202
|
let inMemTable = InMemoryStore.getInMemTable(inMemoryStore, entityConfig);
|
|
207
|
-
let load = async (
|
|
203
|
+
let load = async (filters, param) => {
|
|
208
204
|
let storage = Persistence.getInitializedStorageOrThrow(persistence);
|
|
209
205
|
let timerRef = Prometheus.StorageLoad.startOperation(storage.name, key);
|
|
210
206
|
let size = {
|
|
211
207
|
contents: 0
|
|
212
208
|
};
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
operator: operator
|
|
217
|
-
}));
|
|
218
|
-
await Promise.all(indiciesToLoad.map(async index => {
|
|
219
|
-
InMemoryTable.Entity.addEmptyIndex(inMemTable, index);
|
|
209
|
+
filters.forEach(filter => InMemoryTable.Entity.addEmptyIndex(inMemTable, filter));
|
|
210
|
+
let queries = EntityFilter.merge(filters);
|
|
211
|
+
await Promise.all(queries.map(async filter => {
|
|
220
212
|
try {
|
|
221
|
-
let
|
|
222
|
-
switch (index.operator) {
|
|
223
|
-
case "Eq" :
|
|
224
|
-
tmp = "=";
|
|
225
|
-
break;
|
|
226
|
-
case "Gt" :
|
|
227
|
-
tmp = ">";
|
|
228
|
-
break;
|
|
229
|
-
case "Lt" :
|
|
230
|
-
tmp = "<";
|
|
231
|
-
break;
|
|
232
|
-
}
|
|
233
|
-
let entities = await storage.loadByFieldOrThrow(TableIndices.Index.getFieldName(index), fieldValueSchema, index.fieldValue, tmp, entityConfig.table, entityConfig.rowsSchema);
|
|
213
|
+
let entities = await storage.loadOrThrow(filter, entityConfig.table);
|
|
234
214
|
entities.forEach(entity => InMemoryTable.Entity.initValue(inMemTable, inMemoryStore.committedCheckpointId, entity.id, entity));
|
|
235
215
|
size.contents = size.contents + entities.length | 0;
|
|
236
216
|
return;
|
|
@@ -238,23 +218,21 @@ function loadByField(loadManager, persistence, operator, entityConfig, inMemoryS
|
|
|
238
218
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
239
219
|
if (exn.RE_EXN_ID === Persistence.StorageError) {
|
|
240
220
|
return ErrorHandling.mkLogAndRaise(Logging.createChildFrom(Logging.getItemLogger(item), {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
fieldName: fieldName,
|
|
244
|
-
fieldValue: fieldValue
|
|
221
|
+
operation: key,
|
|
222
|
+
params: EntityFilter.getParams(filter)
|
|
245
223
|
}), exn.message, exn.reason);
|
|
246
224
|
}
|
|
247
225
|
throw exn;
|
|
248
226
|
}
|
|
249
227
|
}));
|
|
250
|
-
return Prometheus.StorageLoad.endOperation(timerRef, storage.name, key,
|
|
228
|
+
return Prometheus.StorageLoad.endOperation(timerRef, storage.name, key, Stdlib_Array.reduce(queries, 0, (acc, query) => acc + EntityFilter.valuesCount(query) | 0), size.contents);
|
|
251
229
|
};
|
|
252
|
-
return LoadManager.call(loadManager,
|
|
230
|
+
return LoadManager.call(loadManager, filter, key, load, EntityFilter.toString, shouldGroup, InMemoryTable.Entity.hasIndex(inMemTable), InMemoryTable.Entity.getUnsafeOnIndex(inMemTable));
|
|
253
231
|
}
|
|
254
232
|
|
|
255
233
|
export {
|
|
256
234
|
loadById,
|
|
257
|
-
|
|
235
|
+
loadByFilter,
|
|
258
236
|
loadEffect,
|
|
259
237
|
}
|
|
260
|
-
/*
|
|
238
|
+
/* Table Not a pure module */
|
package/src/LoadLayer.resi
CHANGED
|
@@ -8,17 +8,14 @@ let loadById: (
|
|
|
8
8
|
~entityId: string,
|
|
9
9
|
) => promise<option<Internal.entity>>
|
|
10
10
|
|
|
11
|
-
let
|
|
11
|
+
let loadByFilter: (
|
|
12
12
|
~loadManager: LoadManager.t,
|
|
13
13
|
~persistence: Persistence.t,
|
|
14
|
-
~operator: TableIndices.Operator.t,
|
|
15
14
|
~entityConfig: Internal.entityConfig,
|
|
16
15
|
~inMemoryStore: InMemoryStore.t,
|
|
17
|
-
~fieldName: string,
|
|
18
|
-
~fieldValueSchema: RescriptSchema.S.t<'fieldValue>,
|
|
19
16
|
~shouldGroup: bool,
|
|
20
17
|
~item: Internal.item,
|
|
21
|
-
~
|
|
18
|
+
~filter: EntityFilter.t,
|
|
22
19
|
) => promise<array<Internal.entity>>
|
|
23
20
|
|
|
24
21
|
let loadEffect: (
|
package/src/LogSelection.res
CHANGED
|
@@ -12,7 +12,7 @@ let makeTopicSelection = (~topic0, ~topic1=[], ~topic2=[], ~topic3=[]) =>
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
let hasFilters = ({topic1, topic2, topic3}: Internal.topicSelection) => {
|
|
15
|
-
[topic1, topic2, topic3]->Array.find(topic => !Utils.Array.isEmpty(topic))->
|
|
15
|
+
[topic1, topic2, topic3]->Array.find(topic => !Utils.Array.isEmpty(topic))->Option.isSome
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/**
|
|
@@ -24,11 +24,11 @@ let compressTopicSelections = (topicSelections: array<Internal.topicSelection>)
|
|
|
24
24
|
|
|
25
25
|
let selectionsWithFilters = []
|
|
26
26
|
|
|
27
|
-
topicSelections->
|
|
27
|
+
topicSelections->Array.forEach(selection => {
|
|
28
28
|
if selection->hasFilters {
|
|
29
29
|
selectionsWithFilters->Array.push(selection)->ignore
|
|
30
30
|
} else {
|
|
31
|
-
selection.topic0->
|
|
31
|
+
selection.topic0->Array.forEach(topic0 => {
|
|
32
32
|
topic0sOfSelectionsWithoutFilters->Array.push(topic0)->ignore
|
|
33
33
|
})
|
|
34
34
|
}
|
|
@@ -43,7 +43,7 @@ let compressTopicSelections = (topicSelections: array<Internal.topicSelection>)
|
|
|
43
43
|
topic2: [],
|
|
44
44
|
topic3: [],
|
|
45
45
|
}
|
|
46
|
-
|
|
46
|
+
Array.concat([selectionWithoutFilters], selectionsWithFilters)
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -60,6 +60,10 @@ let make = (~addresses, ~topicSelections) => {
|
|
|
60
60
|
type parsedEventFilters = {
|
|
61
61
|
getEventFiltersOrThrow: ChainMap.Chain.t => Internal.eventFilters,
|
|
62
62
|
filterByAddresses: bool,
|
|
63
|
+
// Indexed params filtered by `chain.<Contract>.addresses`, in disjunctive
|
|
64
|
+
// normal form (outer array OR of AND-groups). Empty unless `filterByAddresses`.
|
|
65
|
+
// Consumed by the codegen of the event's `clientAddressFilter`.
|
|
66
|
+
addressFilterParamGroups: array<array<string>>,
|
|
63
67
|
// `_gte` from the top-level `block` filter of the user's `where`,
|
|
64
68
|
// resolved at build time (per-chain via the `probeChainId`). The
|
|
65
69
|
// caller uses this to override the per-event `startBlock` — a
|
|
@@ -151,6 +155,70 @@ let makeDetectionChainArg = (
|
|
|
151
155
|
chainObj
|
|
152
156
|
}
|
|
153
157
|
|
|
158
|
+
// Sentinel returned by `chain.<Contract>.addresses` during detection. A Proxy
|
|
159
|
+
// whose traps throw on any access, so the only non-throwing use is passing it
|
|
160
|
+
// straight through as a param filter value — which `extractAddressFilterGroups`
|
|
161
|
+
// then finds by identity. Misuse (spread/map/index/...) fails loud at the site.
|
|
162
|
+
let makeAddressesProbe: (
|
|
163
|
+
~contractName: string,
|
|
164
|
+
) => array<Address.t> = %raw(`function (contractName) {
|
|
165
|
+
var trap = function () {
|
|
166
|
+
throw new Error(
|
|
167
|
+
'Invalid where configuration for "' + contractName +
|
|
168
|
+
'": chain.' + contractName + '.addresses must be passed directly as an indexed-param ' +
|
|
169
|
+
'filter value (e.g. { params: { to: chain.' + contractName + '.addresses } }). ' +
|
|
170
|
+
'It cannot be spread, mapped, indexed, or otherwise transformed.'
|
|
171
|
+
);
|
|
172
|
+
};
|
|
173
|
+
return new Proxy([], {get: trap});
|
|
174
|
+
}`)
|
|
175
|
+
|
|
176
|
+
// Find which indexed params the probed `where` result assigned the Proxy to
|
|
177
|
+
// (DNF: object => one AND-group, array => OR of groups), neutralizing each match
|
|
178
|
+
// to `[]` so later passes can't touch the throwing Proxy. Throws when the
|
|
179
|
+
// callback read the addresses but didn't use them as a param filter value, since
|
|
180
|
+
// the caller only invokes this once it knows the addresses were read.
|
|
181
|
+
let extractAddressFilterGroupsOrThrow = (
|
|
182
|
+
result: JSON.t,
|
|
183
|
+
~probe: array<Address.t>,
|
|
184
|
+
~contractName: string,
|
|
185
|
+
): array<array<string>> => {
|
|
186
|
+
let groups = []
|
|
187
|
+
let scanGroup = (paramsObj: dict<JSON.t>) => {
|
|
188
|
+
let names = []
|
|
189
|
+
paramsObj->Utils.Dict.forEachWithKey((value, key) => {
|
|
190
|
+
if value === probe->(Utils.magic: array<Address.t> => JSON.t) {
|
|
191
|
+
names->Array.push(key)->ignore
|
|
192
|
+
paramsObj->Dict.set(key, []->(Utils.magic: array<unknown> => JSON.t))
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
if names->Utils.Array.isEmpty->not {
|
|
196
|
+
groups->Array.push(names)->ignore
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
switch result {
|
|
200
|
+
| Object(obj) =>
|
|
201
|
+
switch obj->Dict.get("params") {
|
|
202
|
+
| Some(Object(p)) => scanGroup(p)
|
|
203
|
+
| Some(Array(arr)) =>
|
|
204
|
+
arr->Array.forEach(item =>
|
|
205
|
+
switch item {
|
|
206
|
+
| Object(p) => scanGroup(p)
|
|
207
|
+
| _ => ()
|
|
208
|
+
}
|
|
209
|
+
)
|
|
210
|
+
| _ => ()
|
|
211
|
+
}
|
|
212
|
+
| _ => ()
|
|
213
|
+
}
|
|
214
|
+
if groups->Utils.Array.isEmpty {
|
|
215
|
+
JsError.throwWithMessage(
|
|
216
|
+
`Invalid where configuration for ${contractName}. The callback reads \`chain.${contractName}.addresses\` but doesn't use it as an indexed-param filter value. Use it directly, e.g. { params: { to: chain.${contractName}.addresses } }.`,
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
groups
|
|
220
|
+
}
|
|
221
|
+
|
|
154
222
|
let parseEventFiltersOrThrow = {
|
|
155
223
|
let emptyTopics = []
|
|
156
224
|
let noopGetter = _ => emptyTopics
|
|
@@ -167,6 +235,7 @@ let parseEventFiltersOrThrow = {
|
|
|
167
235
|
~topic3=noopGetter,
|
|
168
236
|
): parsedEventFilters => {
|
|
169
237
|
let filterByAddresses = ref(false)
|
|
238
|
+
let addressFilterParamGroups = ref([])
|
|
170
239
|
let startBlock = ref(None)
|
|
171
240
|
let topic0 = [sighash->EvmTypes.Hex.fromStringUnsafe]
|
|
172
241
|
let default = {
|
|
@@ -286,24 +355,23 @@ let parseEventFiltersOrThrow = {
|
|
|
286
355
|
// `startBlock` (from `where.block`) for this chain — a second
|
|
287
356
|
// invocation would risk observing different state for callbacks
|
|
288
357
|
// that close over mutable references.
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
startBlock := extractStartBlock(~onEventBlockFilterSchema, ~contractName, result)
|
|
305
|
-
| None => ()
|
|
358
|
+
// A misused Proxy (or any throw from the callback) propagates as-is —
|
|
359
|
+
// the Proxy's guidance message surfaces without wrapping.
|
|
360
|
+
let addressesProbe = makeAddressesProbe(~contractName)
|
|
361
|
+
let chain = makeDetectionChainArg(
|
|
362
|
+
~contractName,
|
|
363
|
+
~chainId=probeChainId,
|
|
364
|
+
~getAddresses=() => {
|
|
365
|
+
filterByAddresses := true
|
|
366
|
+
addressesProbe
|
|
367
|
+
},
|
|
368
|
+
)
|
|
369
|
+
let probedResult = fn({chain: chain->Obj.magic})
|
|
370
|
+
if filterByAddresses.contents {
|
|
371
|
+
addressFilterParamGroups :=
|
|
372
|
+
extractAddressFilterGroupsOrThrow(probedResult, ~probe=addressesProbe, ~contractName)
|
|
306
373
|
}
|
|
374
|
+
startBlock := extractStartBlock(~onEventBlockFilterSchema, ~contractName, probedResult)
|
|
307
375
|
if filterByAddresses.contents {
|
|
308
376
|
chain => Internal.Dynamic(
|
|
309
377
|
addresses => {
|
|
@@ -343,6 +411,7 @@ let parseEventFiltersOrThrow = {
|
|
|
343
411
|
{
|
|
344
412
|
getEventFiltersOrThrow,
|
|
345
413
|
filterByAddresses: filterByAddresses.contents,
|
|
414
|
+
addressFilterParamGroups: addressFilterParamGroups.contents,
|
|
346
415
|
startBlock: startBlock.contents,
|
|
347
416
|
}
|
|
348
417
|
}
|
package/src/LogSelection.res.mjs
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Utils from "./Utils.res.mjs";
|
|
4
|
-
import * as
|
|
5
|
-
import * as Belt_Option from "@rescript/runtime/lib/es6/Belt_Option.js";
|
|
4
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
5
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
7
6
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
8
7
|
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
@@ -35,7 +34,7 @@ function makeTopicSelection(topic0, topic1Opt, topic2Opt, topic3Opt) {
|
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
function hasFilters(param) {
|
|
38
|
-
return
|
|
37
|
+
return Stdlib_Option.isSome([
|
|
39
38
|
param.topic1,
|
|
40
39
|
param.topic2,
|
|
41
40
|
param.topic3
|
|
@@ -45,12 +44,11 @@ function hasFilters(param) {
|
|
|
45
44
|
function compressTopicSelections(topicSelections) {
|
|
46
45
|
let topic0sOfSelectionsWithoutFilters = [];
|
|
47
46
|
let selectionsWithFilters = [];
|
|
48
|
-
|
|
47
|
+
topicSelections.forEach(selection => {
|
|
49
48
|
if (hasFilters(selection)) {
|
|
50
49
|
selectionsWithFilters.push(selection);
|
|
51
|
-
return;
|
|
52
50
|
} else {
|
|
53
|
-
|
|
51
|
+
selection.topic0.forEach(topic0 => {
|
|
54
52
|
topic0sOfSelectionsWithoutFilters.push(topic0);
|
|
55
53
|
});
|
|
56
54
|
}
|
|
@@ -67,7 +65,7 @@ function compressTopicSelections(topicSelections) {
|
|
|
67
65
|
topic2: selectionWithoutFilters_topic2,
|
|
68
66
|
topic3: selectionWithoutFilters_topic3
|
|
69
67
|
};
|
|
70
|
-
return
|
|
68
|
+
return [selectionWithoutFilters].concat(selectionsWithFilters);
|
|
71
69
|
}
|
|
72
70
|
|
|
73
71
|
function make(addresses, topicSelections) {
|
|
@@ -123,6 +121,58 @@ function makeDetectionChainArg(contractName, chainId, getAddresses) {
|
|
|
123
121
|
return chainObj;
|
|
124
122
|
}
|
|
125
123
|
|
|
124
|
+
let makeAddressesProbe = (function (contractName) {
|
|
125
|
+
var trap = function () {
|
|
126
|
+
throw new Error(
|
|
127
|
+
'Invalid where configuration for "' + contractName +
|
|
128
|
+
'": chain.' + contractName + '.addresses must be passed directly as an indexed-param ' +
|
|
129
|
+
'filter value (e.g. { params: { to: chain.' + contractName + '.addresses } }). ' +
|
|
130
|
+
'It cannot be spread, mapped, indexed, or otherwise transformed.'
|
|
131
|
+
);
|
|
132
|
+
};
|
|
133
|
+
return new Proxy([], {get: trap});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
function extractAddressFilterGroupsOrThrow(result, probe, contractName) {
|
|
137
|
+
let groups = [];
|
|
138
|
+
let scanGroup = paramsObj => {
|
|
139
|
+
let names = [];
|
|
140
|
+
Utils.Dict.forEachWithKey(paramsObj, (value, key) => {
|
|
141
|
+
if (value === probe) {
|
|
142
|
+
names.push(key);
|
|
143
|
+
paramsObj[key] = [];
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
if (!Utils.$$Array.isEmpty(names)) {
|
|
148
|
+
groups.push(names);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
if (typeof result === "object" && result !== null && !Array.isArray(result)) {
|
|
153
|
+
let match = result["params"];
|
|
154
|
+
if (match !== undefined) {
|
|
155
|
+
if (Array.isArray(match)) {
|
|
156
|
+
match.forEach(item => {
|
|
157
|
+
if (typeof item === "object" && item !== null && !Array.isArray(item)) {
|
|
158
|
+
return scanGroup(item);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
} else {
|
|
162
|
+
switch (typeof match) {
|
|
163
|
+
case "object" :
|
|
164
|
+
scanGroup(match);
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (Utils.$$Array.isEmpty(groups)) {
|
|
171
|
+
Stdlib_JsError.throwWithMessage(`Invalid where configuration for ` + contractName + `. The callback reads \`chain.` + contractName + `.addresses\` but doesn't use it as an indexed-param filter value. Use it directly, e.g. { params: { to: chain.` + contractName + `.addresses } }.`);
|
|
172
|
+
}
|
|
173
|
+
return groups;
|
|
174
|
+
}
|
|
175
|
+
|
|
126
176
|
let emptyTopics = [];
|
|
127
177
|
|
|
128
178
|
function noopGetter(param) {
|
|
@@ -136,6 +186,7 @@ function parseEventFiltersOrThrow(eventFilters, sighash, params, contractName, p
|
|
|
136
186
|
let filterByAddresses = {
|
|
137
187
|
contents: false
|
|
138
188
|
};
|
|
189
|
+
let addressFilterParamGroups = [];
|
|
139
190
|
let startBlock;
|
|
140
191
|
let topic0 = [sighash];
|
|
141
192
|
let $$default = {
|
|
@@ -210,21 +261,18 @@ function parseEventFiltersOrThrow(eventFilters, sighash, params, contractName, p
|
|
|
210
261
|
let getEventFiltersOrThrow;
|
|
211
262
|
if (eventFilters !== undefined) {
|
|
212
263
|
if (typeof eventFilters === "function") {
|
|
213
|
-
let
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
probedResult = undefined;
|
|
224
|
-
}
|
|
225
|
-
if (probedResult !== undefined) {
|
|
226
|
-
startBlock = extractStartBlock(probedResult, onEventBlockFilterSchema, contractName);
|
|
264
|
+
let addressesProbe = makeAddressesProbe(contractName);
|
|
265
|
+
let chain = makeDetectionChainArg(contractName, probeChainId, () => {
|
|
266
|
+
filterByAddresses.contents = true;
|
|
267
|
+
return addressesProbe;
|
|
268
|
+
});
|
|
269
|
+
let probedResult = eventFilters({
|
|
270
|
+
chain: chain
|
|
271
|
+
});
|
|
272
|
+
if (filterByAddresses.contents) {
|
|
273
|
+
addressFilterParamGroups = extractAddressFilterGroupsOrThrow(probedResult, addressesProbe, contractName);
|
|
227
274
|
}
|
|
275
|
+
startBlock = extractStartBlock(probedResult, onEventBlockFilterSchema, contractName);
|
|
228
276
|
getEventFiltersOrThrow = filterByAddresses.contents ? chain => ({
|
|
229
277
|
TAG: "Dynamic",
|
|
230
278
|
_0: addresses => {
|
|
@@ -260,6 +308,7 @@ function parseEventFiltersOrThrow(eventFilters, sighash, params, contractName, p
|
|
|
260
308
|
return {
|
|
261
309
|
getEventFiltersOrThrow: getEventFiltersOrThrow,
|
|
262
310
|
filterByAddresses: filterByAddresses.contents,
|
|
311
|
+
addressFilterParamGroups: addressFilterParamGroups,
|
|
263
312
|
startBlock: startBlock
|
|
264
313
|
};
|
|
265
314
|
}
|
|
@@ -274,6 +323,8 @@ export {
|
|
|
274
323
|
extractStartBlock,
|
|
275
324
|
makeChainArg,
|
|
276
325
|
makeDetectionChainArg,
|
|
326
|
+
makeAddressesProbe,
|
|
327
|
+
extractAddressFilterGroupsOrThrow,
|
|
277
328
|
parseEventFiltersOrThrow,
|
|
278
329
|
}
|
|
279
330
|
/* eventBlockRangeSchema Not a pure module */
|
package/src/Logging.res
CHANGED
|
@@ -172,7 +172,7 @@ let getItemLogger = {
|
|
|
172
172
|
"chainId": chain->ChainMap.Chain.toChainId,
|
|
173
173
|
"block": blockNumber,
|
|
174
174
|
"logIndex": logIndex,
|
|
175
|
-
"address": event.srcAddress,
|
|
175
|
+
"address": (event->Internal.toGenericEvent).srcAddress,
|
|
176
176
|
}->createChildParams
|
|
177
177
|
| Block({blockNumber, onBlockConfig}) =>
|
|
178
178
|
{
|
package/src/Main.res
CHANGED
|
@@ -296,6 +296,59 @@ let getGlobalIndexer = (): 'indexer => {
|
|
|
296
296
|
)
|
|
297
297
|
}
|
|
298
298
|
|
|
299
|
+
// SVM identity: `{program, instruction}` from TS or
|
|
300
|
+
// `{instruction: GADT{contract, _0}}` from ReScript. Same two-format dance
|
|
301
|
+
// as the EVM `parseIdentityConfig`, but reading the SVM-native field names.
|
|
302
|
+
let parseSvmIdentityConfig = (identityConfig: 'a) => {
|
|
303
|
+
let raw =
|
|
304
|
+
identityConfig->(
|
|
305
|
+
Utils.magic: 'a => {"program": unknown, "instruction": unknown, "where": option<JSON.t>}
|
|
306
|
+
)
|
|
307
|
+
let (programName, instructionName) = if typeof(raw["program"]) === #string {
|
|
308
|
+
(
|
|
309
|
+
raw["program"]->(Utils.magic: unknown => string),
|
|
310
|
+
raw["instruction"]->(Utils.magic: unknown => string),
|
|
311
|
+
)
|
|
312
|
+
} else {
|
|
313
|
+
let inst = raw["instruction"]->(Utils.magic: unknown => {"contract": string, "_0": string})
|
|
314
|
+
(inst["contract"], inst["_0"])
|
|
315
|
+
}
|
|
316
|
+
let where = raw["where"]
|
|
317
|
+
let eventOptions: option<Internal.eventOptions<_>> = switch where {
|
|
318
|
+
| None => None
|
|
319
|
+
| Some(_) =>
|
|
320
|
+
Some({
|
|
321
|
+
where: ?(where->(Utils.magic: option<JSON.t> => option<_>)),
|
|
322
|
+
})
|
|
323
|
+
}
|
|
324
|
+
(programName, instructionName, eventOptions)
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// onInstruction: delegates to HandlerRegister.setHandler. The SVM analog of
|
|
328
|
+
// onEvent; the registration store keys on `(contractName, eventName)` which
|
|
329
|
+
// for SVM is `(programName, instructionName)`.
|
|
330
|
+
let onInstructionFn = (identityConfig: 'a, handler: 'b) => {
|
|
331
|
+
HandlerRegister.throwIfFinishedRegistration(~methodName="onInstruction")
|
|
332
|
+
let (programName, instructionName, eventOptions) = parseSvmIdentityConfig(identityConfig)
|
|
333
|
+
// The generic dispatch hands every handler `{event, context}`. SVM handlers
|
|
334
|
+
// receive the instruction under `instruction`, so remap the field here; the
|
|
335
|
+
// payload object itself is the `svmInstruction` built in SvmHyperSyncSource.
|
|
336
|
+
let userHandler =
|
|
337
|
+
handler->(
|
|
338
|
+
Utils.magic: 'b => Envio.svmOnInstructionArgs<Internal.handlerContext> => promise<unit>
|
|
339
|
+
)
|
|
340
|
+
HandlerRegister.setHandler(
|
|
341
|
+
~contractName=programName,
|
|
342
|
+
~eventName=instructionName,
|
|
343
|
+
(args: Internal.genericHandlerArgs<Internal.event, Internal.handlerContext>) =>
|
|
344
|
+
userHandler({
|
|
345
|
+
instruction: args.event->(Utils.magic: Internal.event => Envio.svmInstruction),
|
|
346
|
+
context: args.context,
|
|
347
|
+
}),
|
|
348
|
+
~eventOptions,
|
|
349
|
+
)
|
|
350
|
+
}
|
|
351
|
+
|
|
299
352
|
// contractRegister: delegates to HandlerRegister.setContractRegister
|
|
300
353
|
let contractRegisterFn = (identityConfig: 'a, handler: 'b) => {
|
|
301
354
|
HandlerRegister.throwIfFinishedRegistration(~methodName="contractRegister")
|
|
@@ -467,6 +520,7 @@ let getGlobalIndexer = (): 'indexer => {
|
|
|
467
520
|
"description",
|
|
468
521
|
"chainIds",
|
|
469
522
|
"chains",
|
|
523
|
+
"onInstruction",
|
|
470
524
|
"onSlot",
|
|
471
525
|
"~internalAndWillBeRemovedSoon_onRollbackCommit",
|
|
472
526
|
]
|
|
@@ -490,6 +544,7 @@ let getGlobalIndexer = (): 'indexer => {
|
|
|
490
544
|
chains->(Utils.magic: {..} => unknown)
|
|
491
545
|
}
|
|
492
546
|
| "onEvent" => onEventFn->Utils.magic
|
|
547
|
+
| "onInstruction" => onInstructionFn->Utils.magic
|
|
493
548
|
| "contractRegister" => contractRegisterFn->Utils.magic
|
|
494
549
|
| "onBlock" | "onSlot" => onBlockFn->Utils.magic
|
|
495
550
|
| "~internalAndWillBeRemovedSoon_onRollbackCommit" => onRollbackCommitFn->Utils.magic
|
|
@@ -791,8 +846,12 @@ let start = async (
|
|
|
791
846
|
~onError,
|
|
792
847
|
)
|
|
793
848
|
let gsManager =
|
|
794
|
-
globalState->GlobalStateManager.make(
|
|
795
|
-
|
|
849
|
+
globalState->GlobalStateManager.make(
|
|
850
|
+
~reducers=GlobalState.makeReducers(
|
|
851
|
+
~markBatchProcessed=MarkBatchProcessedAdapter.make(~inMemoryStore=ctx.inMemoryStore),
|
|
852
|
+
),
|
|
853
|
+
~onError=exn =>
|
|
854
|
+
onError(exn->ErrorHandling.make(~msg="Indexer has failed with an unexpected error")),
|
|
796
855
|
)
|
|
797
856
|
if shouldUseTui {
|
|
798
857
|
let _rerender = Tui.start(~getState=() => gsManager->GlobalStateManager.getState)
|
package/src/Main.res.mjs
CHANGED
|
@@ -32,6 +32,7 @@ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js
|
|
|
32
32
|
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
33
33
|
import * as GlobalStateManager from "./GlobalStateManager.res.mjs";
|
|
34
34
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
35
|
+
import * as MarkBatchProcessedAdapter from "./adapters/MarkBatchProcessedAdapter.res.mjs";
|
|
35
36
|
|
|
36
37
|
let chainDataSchema = S$RescriptSchema.schema(s => ({
|
|
37
38
|
chainId: s.m(S$RescriptSchema.float),
|
|
@@ -251,6 +252,38 @@ function getGlobalIndexer() {
|
|
|
251
252
|
let match = parseIdentityConfig(identityConfig);
|
|
252
253
|
HandlerRegister.setHandler(match[0], match[1], handler, match[2], undefined);
|
|
253
254
|
};
|
|
255
|
+
let parseSvmIdentityConfig = identityConfig => {
|
|
256
|
+
let match;
|
|
257
|
+
if (typeof identityConfig.program === "string") {
|
|
258
|
+
match = [
|
|
259
|
+
identityConfig.program,
|
|
260
|
+
identityConfig.instruction
|
|
261
|
+
];
|
|
262
|
+
} else {
|
|
263
|
+
let inst = identityConfig.instruction;
|
|
264
|
+
match = [
|
|
265
|
+
inst.contract,
|
|
266
|
+
inst._0
|
|
267
|
+
];
|
|
268
|
+
}
|
|
269
|
+
let where = identityConfig.where;
|
|
270
|
+
let eventOptions = where !== undefined ? ({
|
|
271
|
+
where: where
|
|
272
|
+
}) : undefined;
|
|
273
|
+
return [
|
|
274
|
+
match[0],
|
|
275
|
+
match[1],
|
|
276
|
+
eventOptions
|
|
277
|
+
];
|
|
278
|
+
};
|
|
279
|
+
let onInstructionFn = (identityConfig, handler) => {
|
|
280
|
+
HandlerRegister.throwIfFinishedRegistration("onInstruction");
|
|
281
|
+
let match = parseSvmIdentityConfig(identityConfig);
|
|
282
|
+
HandlerRegister.setHandler(match[0], match[1], args => handler({
|
|
283
|
+
instruction: args.event,
|
|
284
|
+
context: args.context
|
|
285
|
+
}), match[2], undefined);
|
|
286
|
+
};
|
|
254
287
|
let contractRegisterFn = (identityConfig, handler) => {
|
|
255
288
|
HandlerRegister.throwIfFinishedRegistration("contractRegister");
|
|
256
289
|
let match = parseIdentityConfig(identityConfig);
|
|
@@ -346,6 +379,7 @@ function getGlobalIndexer() {
|
|
|
346
379
|
"description",
|
|
347
380
|
"chainIds",
|
|
348
381
|
"chains",
|
|
382
|
+
"onInstruction",
|
|
349
383
|
"onSlot",
|
|
350
384
|
"~internalAndWillBeRemovedSoon_onRollbackCommit"
|
|
351
385
|
];
|
|
@@ -380,6 +414,8 @@ function getGlobalIndexer() {
|
|
|
380
414
|
return Config.loadWithoutRegistrations().name;
|
|
381
415
|
case "onEvent" :
|
|
382
416
|
return onEventFn;
|
|
417
|
+
case "onInstruction" :
|
|
418
|
+
return onInstructionFn;
|
|
383
419
|
case "onBlock" :
|
|
384
420
|
case "onSlot" :
|
|
385
421
|
return onBlockFn;
|
|
@@ -587,7 +623,7 @@ async function start(persistence, resetOpt, isTestOpt, exitAfterFirstEventBlockO
|
|
|
587
623
|
}
|
|
588
624
|
let chainManager = ChainManager.makeFromDbState(Persistence.getInitializedState(persistence$1), config$2, registrations, undefined);
|
|
589
625
|
let globalState = GlobalState.make(ctx, chainManager, isDevelopmentMode, shouldUseTui, exitAfterFirstEventBlock, onError);
|
|
590
|
-
let gsManager = GlobalStateManager.make(globalState, exn => onError(ErrorHandling.make(exn, undefined, "Indexer has failed with an unexpected error")));
|
|
626
|
+
let gsManager = GlobalStateManager.make(globalState, GlobalState.makeReducers(undefined, undefined, undefined, MarkBatchProcessedAdapter.make(ctx_inMemoryStore)), exn => onError(ErrorHandling.make(exn, undefined, "Indexer has failed with an unexpected error")));
|
|
591
627
|
if (shouldUseTui) {
|
|
592
628
|
Tui.start(() => GlobalStateManager.getState(gsManager));
|
|
593
629
|
}
|