envio 3.3.0-alpha.2 → 3.3.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +23 -21
- package/package.json +6 -6
- package/src/ChainFetching.res +25 -0
- package/src/ChainFetching.res.mjs +19 -0
- package/src/CrossChainState.res +15 -0
- package/src/CrossChainState.res.mjs +12 -0
- package/src/Ecosystem.res +0 -2
- package/src/EventConfigBuilder.res +42 -18
- package/src/EventConfigBuilder.res.mjs +21 -9
- package/src/EventProcessing.res +53 -37
- package/src/EventProcessing.res.mjs +41 -36
- package/src/FetchState.res +53 -43
- package/src/FetchState.res.mjs +71 -54
- package/src/HandlerLoader.res +10 -1
- package/src/HandlerLoader.res.mjs +15 -8
- package/src/Internal.res +3 -3
- package/src/Internal.res.mjs +1 -1
- package/src/LoadLayer.res +5 -5
- package/src/LoadLayer.res.mjs +6 -6
- package/src/PgStorage.res +4 -4
- package/src/PgStorage.res.mjs +5 -5
- package/src/Prometheus.res +10 -10
- package/src/Prometheus.res.mjs +9 -9
- package/src/PruneStaleHistory.res +2 -2
- package/src/PruneStaleHistory.res.mjs +3 -3
- package/src/Rollback.res +2 -2
- package/src/Rollback.res.mjs +3 -3
- package/src/SimulateItems.res +77 -15
- package/src/SimulateItems.res.mjs +63 -20
- package/src/TestIndexer.res +48 -36
- package/src/TestIndexer.res.mjs +35 -28
- package/src/bindings/Performance.res +7 -0
- package/src/bindings/Performance.res.mjs +21 -0
- package/src/bindings/Performance.resi +7 -0
- package/src/sources/EventRouter.res +17 -21
- package/src/sources/EventRouter.res.mjs +6 -9
- package/src/sources/Evm.res +1 -35
- package/src/sources/Evm.res.mjs +2 -37
- package/src/sources/Fuel.res +0 -2
- package/src/sources/Fuel.res.mjs +0 -6
- package/src/sources/HyperFuelSource.res +10 -11
- package/src/sources/HyperFuelSource.res.mjs +11 -11
- package/src/sources/HyperSync.res +3 -0
- package/src/sources/HyperSync.res.mjs +1 -0
- package/src/sources/HyperSync.resi +1 -0
- package/src/sources/HyperSyncClient.res +5 -1
- package/src/sources/HyperSyncSource.res +42 -24
- package/src/sources/HyperSyncSource.res.mjs +30 -26
- package/src/sources/RpcSource.res +7 -8
- package/src/sources/RpcSource.res.mjs +8 -8
- package/src/sources/SimulateSource.res +1 -1
- package/src/sources/Source.res +1 -1
- package/src/sources/SourceManager.res +17 -16
- package/src/sources/SourceManager.res.mjs +10 -15
- package/src/sources/Svm.res +4 -10
- package/src/sources/Svm.res.mjs +5 -12
- package/src/sources/SvmHyperSyncSource.res +24 -18
- package/src/sources/SvmHyperSyncSource.res.mjs +22 -15
- package/src/sources/TransactionStore.res +59 -42
- package/src/sources/TransactionStore.res.mjs +21 -1
- package/svm.schema.json +30 -37
- package/src/bindings/Hrtime.res +0 -58
- package/src/bindings/Hrtime.res.mjs +0 -90
- package/src/bindings/Hrtime.resi +0 -30
package/index.d.ts
CHANGED
|
@@ -997,24 +997,6 @@ export type SvmTokenBalance = {
|
|
|
997
997
|
readonly postAmount?: string;
|
|
998
998
|
};
|
|
999
999
|
|
|
1000
|
-
/** Parent transaction surfaced when an instruction's
|
|
1001
|
-
* `include_transaction` flag is `true`. */
|
|
1002
|
-
export type SvmTransaction = {
|
|
1003
|
-
readonly signatures: readonly string[];
|
|
1004
|
-
readonly feePayer?: string;
|
|
1005
|
-
readonly success?: boolean;
|
|
1006
|
-
readonly err?: string;
|
|
1007
|
-
/** Lamports. */
|
|
1008
|
-
readonly fee?: bigint;
|
|
1009
|
-
readonly computeUnitsConsumed?: bigint;
|
|
1010
|
-
readonly accountKeys: readonly string[];
|
|
1011
|
-
readonly recentBlockhash?: string;
|
|
1012
|
-
readonly version?: string;
|
|
1013
|
-
/** SPL Token / Token-2022 balance snapshots for this transaction.
|
|
1014
|
-
* Present when `include_token_balances` is `true`. */
|
|
1015
|
-
readonly tokenBalances?: readonly SvmTokenBalance[];
|
|
1016
|
-
};
|
|
1017
|
-
|
|
1018
1000
|
export type SvmLog = {
|
|
1019
1001
|
readonly kind: string;
|
|
1020
1002
|
readonly message: string;
|
|
@@ -1033,6 +1015,7 @@ export type SvmLog = {
|
|
|
1033
1015
|
* are base58 strings. */
|
|
1034
1016
|
export type SvmInstruction<
|
|
1035
1017
|
Params extends SvmInstructionParams = SvmInstructionParams,
|
|
1018
|
+
Tx = SvmTransaction,
|
|
1036
1019
|
> = {
|
|
1037
1020
|
/** Program name as declared under `programs[].name` in `config.yaml`. */
|
|
1038
1021
|
readonly programName: string;
|
|
@@ -1050,8 +1033,11 @@ export type SvmInstruction<
|
|
|
1050
1033
|
readonly d8?: string;
|
|
1051
1034
|
/** Borsh-decoded params. Present when a schema is configured and matched. */
|
|
1052
1035
|
readonly params?: Params;
|
|
1053
|
-
/**
|
|
1054
|
-
|
|
1036
|
+
/** Parent transaction. Carries only the fields selected via this
|
|
1037
|
+
* instruction's `field_selection`; unselected fields are typed as
|
|
1038
|
+
* `FieldNotSelected<...>` so reading them is a compile error. Always present
|
|
1039
|
+
* (`{}` when no fields are selected). */
|
|
1040
|
+
readonly transaction: Tx;
|
|
1055
1041
|
/** Present when the instruction's `include_logs` is `true`; only logs
|
|
1056
1042
|
* scoped to this exact instruction (matching `instruction_address`). */
|
|
1057
1043
|
readonly logs?: readonly SvmLog[];
|
|
@@ -1327,7 +1313,10 @@ type SvmEcosystem<Config extends IndexerConfigTypes = GlobalConfig> =
|
|
|
1327
1313
|
handler: (
|
|
1328
1314
|
args: SvmOnInstructionHandlerArgs<
|
|
1329
1315
|
Config,
|
|
1330
|
-
SvmInstruction<
|
|
1316
|
+
SvmInstruction<
|
|
1317
|
+
SvmParamsFromProgramTable<Programs[P][I]>,
|
|
1318
|
+
Programs[P][I]["transaction"]
|
|
1319
|
+
>
|
|
1331
1320
|
>,
|
|
1332
1321
|
) => Promise<void>,
|
|
1333
1322
|
) => void;
|
|
@@ -1695,6 +1684,7 @@ type EvmContractsT = GlobalConfig extends { evm: { contracts: infer X extends
|
|
|
1695
1684
|
type FuelChainsT = GlobalConfig extends { fuel: { chains: infer X extends Record<string, { id: number }> } } ? X : {};
|
|
1696
1685
|
type FuelContractsT = GlobalConfig extends { fuel: { contracts: infer X extends Record<string, Record<string, any>> } } ? X : {};
|
|
1697
1686
|
type SvmChainsT = GlobalConfig extends { svm: { chains: infer X extends Record<string, { id: number }> } } ? X : {};
|
|
1687
|
+
type SvmProgramsT = GlobalConfig extends { svm: { programs: infer X extends Record<string, Record<string, any>> } } ? X : {};
|
|
1698
1688
|
type EntitiesT = GlobalConfig extends { entities: infer X extends Record<string, object> } ? X : {};
|
|
1699
1689
|
type EnumsT = GlobalConfig extends { enums: infer X extends Record<string, any> } ? X : {};
|
|
1700
1690
|
|
|
@@ -1716,6 +1706,18 @@ export type FuelChainId = IsEmptyObject<FuelChainsT> extends true ? NotConfigure
|
|
|
1716
1706
|
/** Union of all configured SVM chain IDs. */
|
|
1717
1707
|
export type SvmChainId = IsEmptyObject<SvmChainsT> extends true ? NotConfigured<"SvmChainId", "Configure SVM chains"> : SvmChainsT [keyof SvmChainsT ]["id"];
|
|
1718
1708
|
|
|
1709
|
+
/** The SVM parent-transaction type generated from this project's
|
|
1710
|
+
* `field_selection`: the union of every instruction's `transaction` shape,
|
|
1711
|
+
* with unselected fields typed as `FieldNotSelected<...>`. Resolves to a
|
|
1712
|
+
* `NotConfigured` hint until `envio codegen` augments {@link Global}. */
|
|
1713
|
+
export type SvmTransaction = IsEmptyObject<SvmProgramsT> extends true
|
|
1714
|
+
? NotConfigured<"SvmTransaction", "Configure SVM programs">
|
|
1715
|
+
: {
|
|
1716
|
+
[P in keyof SvmProgramsT]: {
|
|
1717
|
+
[I in keyof SvmProgramsT[P]]: SvmProgramsT[P][I]["transaction"];
|
|
1718
|
+
}[keyof SvmProgramsT[P]];
|
|
1719
|
+
}[keyof SvmProgramsT];
|
|
1720
|
+
|
|
1719
1721
|
/** Lookup an EVM event type by contract and event name. Without generics,
|
|
1720
1722
|
* resolves to the discriminated union of every EVM event in the project. */
|
|
1721
1723
|
export type EvmEvent<
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "envio",
|
|
3
|
-
"version": "3.3.0-alpha.
|
|
3
|
+
"version": "3.3.0-alpha.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
|
|
6
6
|
"bin": "./bin.mjs",
|
|
@@ -69,10 +69,10 @@
|
|
|
69
69
|
"tsx": "4.21.0"
|
|
70
70
|
},
|
|
71
71
|
"optionalDependencies": {
|
|
72
|
-
"envio-linux-x64": "3.3.0-alpha.
|
|
73
|
-
"envio-linux-x64-musl": "3.3.0-alpha.
|
|
74
|
-
"envio-linux-arm64": "3.3.0-alpha.
|
|
75
|
-
"envio-darwin-x64": "3.3.0-alpha.
|
|
76
|
-
"envio-darwin-arm64": "3.3.0-alpha.
|
|
72
|
+
"envio-linux-x64": "3.3.0-alpha.3",
|
|
73
|
+
"envio-linux-x64-musl": "3.3.0-alpha.3",
|
|
74
|
+
"envio-linux-arm64": "3.3.0-alpha.3",
|
|
75
|
+
"envio-darwin-x64": "3.3.0-alpha.3",
|
|
76
|
+
"envio-darwin-arm64": "3.3.0-alpha.3"
|
|
77
77
|
}
|
|
78
78
|
}
|
package/src/ChainFetching.res
CHANGED
|
@@ -336,6 +336,15 @@ let fetchChain = async (
|
|
|
336
336
|
// there's nothing to fetch. During backfill any such chain is idle.
|
|
337
337
|
let reducedPolling = !isRealtime
|
|
338
338
|
|
|
339
|
+
// Accumulated across all queries dispatched in this tick so their per-query
|
|
340
|
+
// results collapse into a single completion log instead of one per query.
|
|
341
|
+
let dispatchedCount = switch action {
|
|
342
|
+
| FetchState.Ready(queries) => queries->Array.length
|
|
343
|
+
| WaitingForNewBlock | NothingToQuery => 0
|
|
344
|
+
}
|
|
345
|
+
let fetchedByPartition = Dict.make()
|
|
346
|
+
let fetchedCount = ref(0)
|
|
347
|
+
|
|
339
348
|
// Owns its error boundary: launch doesn't catch, so any failure here (the
|
|
340
349
|
// query, response handling, or dispatch itself) must stop the indexer.
|
|
341
350
|
try {
|
|
@@ -361,6 +370,22 @@ let fetchChain = async (
|
|
|
361
370
|
~knownHeight=chainState->ChainState.knownHeight,
|
|
362
371
|
~isRealtime,
|
|
363
372
|
)
|
|
373
|
+
fetchedCount := fetchedCount.contents + 1
|
|
374
|
+
fetchedByPartition->Dict.set(
|
|
375
|
+
query.partitionId,
|
|
376
|
+
{
|
|
377
|
+
"fromBlock": response.fromBlockQueried,
|
|
378
|
+
"toBlock": response.latestFetchedBlockNumber,
|
|
379
|
+
"numEvents": response.parsedQueueItems->Array.length,
|
|
380
|
+
},
|
|
381
|
+
)
|
|
382
|
+
if dispatchedCount > 0 && fetchedCount.contents === dispatchedCount {
|
|
383
|
+
Logging.trace({
|
|
384
|
+
"msg": "Finished querying",
|
|
385
|
+
"chainId": chain->ChainMap.Chain.toChainId,
|
|
386
|
+
"partitions": fetchedByPartition,
|
|
387
|
+
})
|
|
388
|
+
}
|
|
364
389
|
await onQueryResponse(
|
|
365
390
|
state,
|
|
366
391
|
{chain, response, query},
|
|
@@ -176,10 +176,29 @@ async function fetchChain(state, chain, action, stateId, scheduleFetch, schedule
|
|
|
176
176
|
let isRealtime = IndexerState.isRealtime(state);
|
|
177
177
|
let sourceManager = ChainState.sourceManager(chainState);
|
|
178
178
|
let reducedPolling = !isRealtime;
|
|
179
|
+
let dispatchedCount;
|
|
180
|
+
dispatchedCount = typeof action !== "object" ? 0 : action._0.length;
|
|
181
|
+
let fetchedByPartition = {};
|
|
182
|
+
let fetchedCount = {
|
|
183
|
+
contents: 0
|
|
184
|
+
};
|
|
179
185
|
try {
|
|
180
186
|
return await ChainState.dispatch(chainState, async query => {
|
|
181
187
|
try {
|
|
182
188
|
let response = await SourceManager.executeQuery(sourceManager, query, ChainState.knownHeight(chainState), isRealtime);
|
|
189
|
+
fetchedCount.contents = fetchedCount.contents + 1 | 0;
|
|
190
|
+
fetchedByPartition[query.partitionId] = {
|
|
191
|
+
fromBlock: response.fromBlockQueried,
|
|
192
|
+
toBlock: response.latestFetchedBlockNumber,
|
|
193
|
+
numEvents: response.parsedQueueItems.length
|
|
194
|
+
};
|
|
195
|
+
if (dispatchedCount > 0 && fetchedCount.contents === dispatchedCount) {
|
|
196
|
+
Logging.trace({
|
|
197
|
+
msg: "Finished querying",
|
|
198
|
+
chainId: chain,
|
|
199
|
+
partitions: fetchedByPartition
|
|
200
|
+
});
|
|
201
|
+
}
|
|
183
202
|
return await onQueryResponse(state, {
|
|
184
203
|
chain: chain,
|
|
185
204
|
response: response,
|
package/src/CrossChainState.res
CHANGED
|
@@ -254,6 +254,21 @@ let checkAndFetch = async (
|
|
|
254
254
|
idx := idx.contents + 1
|
|
255
255
|
}
|
|
256
256
|
admittedByChain->Dict.forEachWithKey((queries, chainId) => {
|
|
257
|
+
let partitions = Dict.make()
|
|
258
|
+
queries->Array.forEach((query: FetchState.query) =>
|
|
259
|
+
partitions->Dict.set(
|
|
260
|
+
query.partitionId,
|
|
261
|
+
{
|
|
262
|
+
"fromBlock": query.fromBlock,
|
|
263
|
+
"targetBlock": query.toBlock,
|
|
264
|
+
},
|
|
265
|
+
)
|
|
266
|
+
)
|
|
267
|
+
Logging.trace({
|
|
268
|
+
"msg": "Started querying",
|
|
269
|
+
"chainId": chainId->Int.fromString->Option.getUnsafe,
|
|
270
|
+
"partitions": partitions,
|
|
271
|
+
})
|
|
257
272
|
actionByChain->Dict.set(chainId, FetchState.Ready(queries))
|
|
258
273
|
// Mark the admitted queries in flight and reserve their size against the
|
|
259
274
|
// shared budget; released as each response lands in handleQueryResult.
|
|
@@ -170,6 +170,18 @@ async function checkAndFetch(crossChainState, dispatchChain) {
|
|
|
170
170
|
idx = idx + 1 | 0;
|
|
171
171
|
};
|
|
172
172
|
Stdlib_Dict.forEachWithKey(admittedByChain, (queries, chainId) => {
|
|
173
|
+
let partitions = {};
|
|
174
|
+
queries.forEach(query => {
|
|
175
|
+
partitions[query.partitionId] = {
|
|
176
|
+
fromBlock: query.fromBlock,
|
|
177
|
+
targetBlock: query.toBlock
|
|
178
|
+
};
|
|
179
|
+
});
|
|
180
|
+
Logging.trace({
|
|
181
|
+
msg: "Started querying",
|
|
182
|
+
chainId: Stdlib_Int.fromString(chainId, undefined),
|
|
183
|
+
partitions: partitions
|
|
184
|
+
});
|
|
173
185
|
actionByChain[chainId] = {
|
|
174
186
|
TAG: "Ready",
|
|
175
187
|
_0: queries
|
package/src/Ecosystem.res
CHANGED
|
@@ -326,28 +326,50 @@ let compileAddressFilter: string => (
|
|
|
326
326
|
return new Function("event", "blockNumber", "indexingAddresses", body);
|
|
327
327
|
}`)
|
|
328
328
|
|
|
329
|
-
// Body of the client-side address filter
|
|
330
|
-
//
|
|
331
|
-
// registered
|
|
332
|
-
//
|
|
333
|
-
//
|
|
334
|
-
|
|
335
|
-
|
|
329
|
+
// Body of the client-side address filter. Two analogous registered-at-or-before
|
|
330
|
+
// checks, ANDed: (1) for non-wildcard events, the log's srcAddress must itself be
|
|
331
|
+
// registered (ownership is resolved structurally by partition, but the temporal
|
|
332
|
+
// `effectiveStartBlock` gate lives here now); (2) a DNF of address-filtered param
|
|
333
|
+
// names (OR of AND-groups) for events that filter an indexed address param. The
|
|
334
|
+
// DNF is fixed here, so it's unrolled into one boolean expression — no per-event
|
|
335
|
+
// closure, loop, or array. `None` only for wildcard events without a param
|
|
336
|
+
// filter. Exposed for snapshotting.
|
|
337
|
+
// `srcAddressExpr` is the JS expression for the event's owning address: EVM and
|
|
338
|
+
// Fuel events expose `event.srcAddress`; SVM instructions expose `event.programId`.
|
|
339
|
+
let buildAddressFilterBody = (
|
|
340
|
+
groups: array<array<string>>,
|
|
341
|
+
~isWildcard: bool,
|
|
342
|
+
~srcAddressExpr: string="event.srcAddress",
|
|
343
|
+
): option<string> => {
|
|
344
|
+
let paramLeaf = name =>
|
|
345
|
+
`(ic = indexingAddresses[p[${JSON.stringify(
|
|
346
|
+
JSON.String(name),
|
|
347
|
+
)}]]) !== undefined && ic.effectiveStartBlock <= blockNumber`
|
|
348
|
+
let paramDnf = switch groups {
|
|
336
349
|
| [] => None
|
|
337
350
|
| _ =>
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
351
|
+
Some(
|
|
352
|
+
groups
|
|
353
|
+
->Array.map(group => "(" ++ group->Array.map(paramLeaf)->Array.join(" && ") ++ ")")
|
|
354
|
+
->Array.join(" || "),
|
|
355
|
+
)
|
|
356
|
+
}
|
|
357
|
+
let srcLeaf = `(ic = indexingAddresses[${srcAddressExpr}]) !== undefined && ic.effectiveStartBlock <= blockNumber`
|
|
358
|
+
switch (isWildcard, paramDnf) {
|
|
359
|
+
| (true, None) => None
|
|
360
|
+
| (true, Some(dnf)) => Some("var p = event.params, ic; return " ++ dnf ++ ";")
|
|
361
|
+
| (false, None) => Some("var ic; return " ++ srcLeaf ++ ";")
|
|
362
|
+
| (false, Some(dnf)) =>
|
|
363
|
+
Some("var p = event.params, ic; return " ++ srcLeaf ++ " && (" ++ dnf ++ ");")
|
|
345
364
|
}
|
|
346
365
|
}
|
|
347
366
|
|
|
348
|
-
let buildAddressFilter = (
|
|
349
|
-
|
|
350
|
-
|
|
367
|
+
let buildAddressFilter = (
|
|
368
|
+
groups: array<array<string>>,
|
|
369
|
+
~isWildcard: bool,
|
|
370
|
+
~srcAddressExpr: string="event.srcAddress",
|
|
371
|
+
): option<(Internal.eventPayload, int, dict<Internal.indexingContract>) => bool> =>
|
|
372
|
+
buildAddressFilterBody(groups, ~isWildcard, ~srcAddressExpr)->Option.map(compileAddressFilter)
|
|
351
373
|
|
|
352
374
|
// ============== Build complete EVM event config ==============
|
|
353
375
|
|
|
@@ -415,7 +437,7 @@ let buildEvmEventConfig = (
|
|
|
415
437
|
simulateParamsSchema: buildSimulateParamsSchema(params),
|
|
416
438
|
getEventFiltersOrThrow,
|
|
417
439
|
filterByAddresses,
|
|
418
|
-
clientAddressFilter: ?buildAddressFilter(addressFilterParamGroups),
|
|
440
|
+
clientAddressFilter: ?buildAddressFilter(addressFilterParamGroups, ~isWildcard),
|
|
419
441
|
dependsOnAddresses: !isWildcard || filterByAddresses,
|
|
420
442
|
startBlock: resolvedStartBlock,
|
|
421
443
|
selectedBlockFields,
|
|
@@ -471,6 +493,7 @@ let buildSvmInstructionEventConfig = (
|
|
|
471
493
|
simulateParamsSchema: paramsSchema,
|
|
472
494
|
filterByAddresses: false,
|
|
473
495
|
dependsOnAddresses: !isWildcard,
|
|
496
|
+
clientAddressFilter: ?buildAddressFilter([], ~isWildcard, ~srcAddressExpr="event.programId"),
|
|
474
497
|
startBlock,
|
|
475
498
|
programId,
|
|
476
499
|
discriminator,
|
|
@@ -541,6 +564,7 @@ let buildFuelEventConfig = (
|
|
|
541
564
|
simulateParamsSchema: paramsSchema,
|
|
542
565
|
filterByAddresses: false,
|
|
543
566
|
dependsOnAddresses: !isWildcard,
|
|
567
|
+
clientAddressFilter: ?buildAddressFilter([], ~isWildcard),
|
|
544
568
|
startBlock,
|
|
545
569
|
// Fuel keeps the transaction inline on the payload; nothing to materialise.
|
|
546
570
|
selectedTransactionFields: Utils.Set.make(),
|
|
@@ -255,17 +255,27 @@ let compileAddressFilter = (function (body) {
|
|
|
255
255
|
return new Function("event", "blockNumber", "indexingAddresses", body);
|
|
256
256
|
});
|
|
257
257
|
|
|
258
|
-
function buildAddressFilterBody(groups) {
|
|
259
|
-
|
|
260
|
-
|
|
258
|
+
function buildAddressFilterBody(groups, isWildcard, srcAddressExprOpt) {
|
|
259
|
+
let srcAddressExpr = srcAddressExprOpt !== undefined ? srcAddressExprOpt : "event.srcAddress";
|
|
260
|
+
let paramLeaf = name => `(ic = indexingAddresses[p[` + JSON.stringify(name) + `]]) !== undefined && ic.effectiveStartBlock <= blockNumber`;
|
|
261
|
+
let paramDnf = groups.length !== 0 ? groups.map(group => "(" + group.map(paramLeaf).join(" && ") + ")").join(" || ") : undefined;
|
|
262
|
+
let srcLeaf = `(ic = indexingAddresses[` + srcAddressExpr + `]) !== undefined && ic.effectiveStartBlock <= blockNumber`;
|
|
263
|
+
if (isWildcard) {
|
|
264
|
+
if (paramDnf !== undefined) {
|
|
265
|
+
return "var p = event.params, ic; return " + paramDnf + ";";
|
|
266
|
+
} else {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
} else if (paramDnf !== undefined) {
|
|
270
|
+
return "var p = event.params, ic; return " + srcLeaf + " && (" + paramDnf + ");";
|
|
271
|
+
} else {
|
|
272
|
+
return "var ic; return " + srcLeaf + ";";
|
|
261
273
|
}
|
|
262
|
-
let leaf = name => `(ic = indexingAddresses[p[` + JSON.stringify(name) + `]]) !== undefined && ic.effectiveStartBlock <= blockNumber`;
|
|
263
|
-
let groupExprs = groups.map(group => "(" + group.map(leaf).join(" && ") + ")");
|
|
264
|
-
return "var p = event.params, ic; return " + groupExprs.join(" || ") + ";";
|
|
265
274
|
}
|
|
266
275
|
|
|
267
|
-
function buildAddressFilter(groups) {
|
|
268
|
-
|
|
276
|
+
function buildAddressFilter(groups, isWildcard, srcAddressExprOpt) {
|
|
277
|
+
let srcAddressExpr = srcAddressExprOpt !== undefined ? srcAddressExprOpt : "event.srcAddress";
|
|
278
|
+
return Stdlib_Option.map(buildAddressFilterBody(groups, isWildcard, srcAddressExpr), compileAddressFilter);
|
|
269
279
|
}
|
|
270
280
|
|
|
271
281
|
function buildEvmEventConfig(contractName, eventName, sighash, params, isWildcard, handler, contractRegister, eventFilters, probeChainId, onEventBlockFilterSchema, blockFields, transactionFields, startBlock, globalBlockFieldsSetOpt, globalTransactionFieldsSetOpt) {
|
|
@@ -291,7 +301,7 @@ function buildEvmEventConfig(contractName, eventName, sighash, params, isWildcar
|
|
|
291
301
|
isWildcard: isWildcard,
|
|
292
302
|
filterByAddresses: filterByAddresses,
|
|
293
303
|
dependsOnAddresses: !isWildcard || filterByAddresses,
|
|
294
|
-
clientAddressFilter:
|
|
304
|
+
clientAddressFilter: buildAddressFilter(match.addressFilterParamGroups, isWildcard, undefined),
|
|
295
305
|
handler: handler,
|
|
296
306
|
contractRegister: contractRegister,
|
|
297
307
|
paramsRawEventSchema: buildParamsSchema(params),
|
|
@@ -320,6 +330,7 @@ function buildSvmInstructionEventConfig(contractName, instructionName, programId
|
|
|
320
330
|
isWildcard: isWildcard,
|
|
321
331
|
filterByAddresses: false,
|
|
322
332
|
dependsOnAddresses: !isWildcard,
|
|
333
|
+
clientAddressFilter: buildAddressFilter([], isWildcard, "event.programId"),
|
|
323
334
|
handler: handler,
|
|
324
335
|
contractRegister: contractRegister,
|
|
325
336
|
paramsRawEventSchema: paramsSchema,
|
|
@@ -388,6 +399,7 @@ function buildFuelEventConfig(contractName, eventName, kind, sighash, rawAbi, is
|
|
|
388
399
|
isWildcard: isWildcard,
|
|
389
400
|
filterByAddresses: false,
|
|
390
401
|
dependsOnAddresses: !isWildcard,
|
|
402
|
+
clientAddressFilter: buildAddressFilter([], isWildcard, undefined),
|
|
391
403
|
handler: handler,
|
|
392
404
|
contractRegister: contractRegister,
|
|
393
405
|
paramsRawEventSchema: paramsSchema,
|
package/src/EventProcessing.res
CHANGED
|
@@ -39,7 +39,7 @@ let runEventHandlerOrThrow = async (
|
|
|
39
39
|
let eventItem = item->Internal.castUnsafeEventItem
|
|
40
40
|
|
|
41
41
|
//Include the load in time before handler
|
|
42
|
-
let timeBeforeHandler =
|
|
42
|
+
let timeBeforeHandler = Performance.now()
|
|
43
43
|
|
|
44
44
|
try {
|
|
45
45
|
let contextParams: UserContext.contextParams = {
|
|
@@ -72,7 +72,7 @@ let runEventHandlerOrThrow = async (
|
|
|
72
72
|
}),
|
|
73
73
|
)
|
|
74
74
|
}
|
|
75
|
-
let handlerDuration = timeBeforeHandler->
|
|
75
|
+
let handlerDuration = timeBeforeHandler->Performance.secondsSince
|
|
76
76
|
Prometheus.ProcessingHandler.increment(
|
|
77
77
|
~contract=eventItem.eventConfig.contractName,
|
|
78
78
|
~event=eventItem.eventConfig.name,
|
|
@@ -264,11 +264,19 @@ let runBatchHandlersOrThrow = async (
|
|
|
264
264
|
}
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
-
let registerProcessEventBatchMetrics = (
|
|
268
|
-
logger
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
267
|
+
let registerProcessEventBatchMetrics = (
|
|
268
|
+
~logger,
|
|
269
|
+
~batch: Batch.t,
|
|
270
|
+
~loadDuration,
|
|
271
|
+
~handlerDuration,
|
|
272
|
+
) => {
|
|
273
|
+
batch.progressedChainsById->Dict.forEachWithKey((chainAfterBatch, chainId) => {
|
|
274
|
+
logger->Logging.childTrace({
|
|
275
|
+
"msg": "Finished processing",
|
|
276
|
+
"chainId": chainId->Int.fromString->Option.getUnsafe,
|
|
277
|
+
"batchSize": chainAfterBatch.batchSize,
|
|
278
|
+
"progress": chainAfterBatch.progressBlockNumber,
|
|
279
|
+
})
|
|
272
280
|
})
|
|
273
281
|
|
|
274
282
|
Prometheus.ProcessingBatch.registerMetrics(~loadDuration, ~handlerDuration)
|
|
@@ -286,22 +294,28 @@ type logPartitionInfo = {
|
|
|
286
294
|
// handlers read plain objects. A batch can span chains, each with its own store
|
|
287
295
|
// and field mask, so group items by chain before materialising.
|
|
288
296
|
let materializeBatchTransactions = async (batch: Batch.t, ~chainStates: dict<ChainState.t>) => {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
297
|
+
switch chainStates->Dict.valuesToArray {
|
|
298
|
+
// Single-chain indexers (the common case): every item belongs to the one
|
|
299
|
+
// chain, so skip the per-chain grouping and its allocations.
|
|
300
|
+
| [cs] => await cs->ChainState.materializeBatchItems(~items=batch.items)
|
|
301
|
+
| _ =>
|
|
302
|
+
let itemsByChain: dict<array<Internal.item>> = Dict.make()
|
|
303
|
+
batch.items->Array.forEach(item => {
|
|
304
|
+
let chainId = item->Internal.getItemChainId->Int.toString
|
|
305
|
+
switch itemsByChain->Utils.Dict.dangerouslyGetNonOption(chainId) {
|
|
306
|
+
| Some(items) => items->Array.push(item)
|
|
307
|
+
| None => itemsByChain->Dict.set(chainId, [item])
|
|
308
|
+
}
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
let _ = await itemsByChain
|
|
312
|
+
->Dict.toArray
|
|
313
|
+
->Array.map(async ((chainId, items)) => {
|
|
314
|
+
let cs = chainStates->Dict.getUnsafe(chainId)
|
|
315
|
+
await cs->ChainState.materializeBatchItems(~items)
|
|
316
|
+
})
|
|
317
|
+
->Promise.all
|
|
318
|
+
}
|
|
305
319
|
}
|
|
306
320
|
|
|
307
321
|
let processEventBatch = async (
|
|
@@ -312,27 +326,24 @@ let processEventBatch = async (
|
|
|
312
326
|
~config: Config.t,
|
|
313
327
|
~chainStates: dict<ChainState.t>,
|
|
314
328
|
) => {
|
|
315
|
-
let totalBatchSize = batch.totalBatchSize
|
|
316
329
|
// Compute chains state for this batch
|
|
317
330
|
let chains: Internal.chains = chainStates->computeChainsState
|
|
318
331
|
|
|
319
332
|
let logger = Logging.getLogger()
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}
|
|
328
|
-
}),
|
|
333
|
+
|
|
334
|
+
batch.progressedChainsById->Dict.forEachWithKey((chainAfterBatch, chainId) => {
|
|
335
|
+
logger->Logging.childTrace({
|
|
336
|
+
"msg": "Started processing",
|
|
337
|
+
"chainId": chainId->Int.fromString->Option.getUnsafe,
|
|
338
|
+
"batchSize": chainAfterBatch.batchSize,
|
|
339
|
+
})
|
|
329
340
|
})
|
|
330
341
|
|
|
331
342
|
try {
|
|
332
343
|
// Backpressure: keep processing within keepLatestChangesLimit of the cycle.
|
|
333
344
|
await indexerState->Writing.awaitCapacity
|
|
334
345
|
|
|
335
|
-
let timeRef =
|
|
346
|
+
let timeRef = Performance.now()
|
|
336
347
|
|
|
337
348
|
if batch.items->Utils.Array.notEmpty {
|
|
338
349
|
// Materialise store-backed transactions onto payloads before any handler
|
|
@@ -341,7 +352,7 @@ let processEventBatch = async (
|
|
|
341
352
|
await batch->preloadBatchOrThrow(~loadManager, ~persistence, ~indexerState, ~chains, ~config)
|
|
342
353
|
}
|
|
343
354
|
|
|
344
|
-
let elapsedTimeAfterLoaders = timeRef->
|
|
355
|
+
let elapsedTimeAfterLoaders = timeRef->Performance.secondsSince
|
|
345
356
|
|
|
346
357
|
if batch.items->Utils.Array.notEmpty {
|
|
347
358
|
await batch->runBatchHandlersOrThrow(
|
|
@@ -353,13 +364,18 @@ let processEventBatch = async (
|
|
|
353
364
|
)
|
|
354
365
|
}
|
|
355
366
|
|
|
356
|
-
let elapsedTimeAfterProcessing = timeRef->
|
|
367
|
+
let elapsedTimeAfterProcessing = timeRef->Performance.secondsSince
|
|
357
368
|
|
|
358
369
|
indexerState->Writing.commitBatch(~batch)
|
|
359
370
|
|
|
360
371
|
let loaderDuration = elapsedTimeAfterLoaders
|
|
361
372
|
let handlerDuration = elapsedTimeAfterProcessing -. loaderDuration
|
|
362
|
-
registerProcessEventBatchMetrics(
|
|
373
|
+
registerProcessEventBatchMetrics(
|
|
374
|
+
~logger,
|
|
375
|
+
~batch,
|
|
376
|
+
~loadDuration=loaderDuration,
|
|
377
|
+
~handlerDuration,
|
|
378
|
+
)
|
|
363
379
|
Ok()
|
|
364
380
|
} catch {
|
|
365
381
|
| Persistence.StorageError({message, reason}) =>
|