envio 3.3.0-alpha.0 → 3.3.0-alpha.10
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 +10 -0
- package/index.d.ts +45 -25
- package/package.json +7 -7
- package/src/Address.res +5 -2
- package/src/Address.res.mjs +3 -1
- package/src/Api.res +1 -1
- package/src/Api.res.mjs +1 -1
- package/src/Batch.res +0 -6
- package/src/Batch.res.mjs +1 -12
- package/src/BatchProcessing.res +7 -12
- package/src/BatchProcessing.res.mjs +8 -7
- package/src/Bin.res +3 -0
- package/src/Bin.res.mjs +4 -0
- package/src/ChainFetching.res +48 -16
- package/src/ChainFetching.res.mjs +36 -17
- package/src/ChainMetadata.res +1 -11
- package/src/ChainMetadata.res.mjs +1 -10
- package/src/ChainState.res +628 -149
- package/src/ChainState.res.mjs +440 -102
- package/src/ChainState.resi +74 -4
- package/src/Config.res +61 -33
- package/src/Config.res.mjs +50 -10
- package/src/ContractRegisterContext.res +2 -12
- package/src/ContractRegisterContext.res.mjs +3 -5
- package/src/Core.res +14 -3
- package/src/CrossChainState.res +142 -93
- package/src/CrossChainState.res.mjs +88 -60
- package/src/CrossChainState.resi +2 -10
- package/src/Ecosystem.res +5 -6
- package/src/Ecosystem.res.mjs +3 -3
- package/src/Env.res +0 -7
- package/src/Env.res.mjs +0 -6
- package/src/Envio.res +17 -11
- package/src/EnvioGlobal.res +53 -0
- package/src/EnvioGlobal.res.mjs +31 -0
- package/src/EventConfigBuilder.res +177 -88
- package/src/EventConfigBuilder.res.mjs +99 -42
- package/src/EventProcessing.res +74 -28
- package/src/EventProcessing.res.mjs +55 -29
- package/src/ExitOnCaughtUp.res +10 -2
- package/src/ExitOnCaughtUp.res.mjs +10 -1
- package/src/FetchState.res +558 -300
- package/src/FetchState.res.mjs +437 -423
- package/src/HandlerLoader.res +8 -104
- package/src/HandlerLoader.res.mjs +2 -81
- package/src/HandlerRegister.res +518 -144
- package/src/HandlerRegister.res.mjs +285 -133
- package/src/HandlerRegister.resi +24 -8
- package/src/IndexerLoop.res +2 -3
- package/src/IndexerLoop.res.mjs +1 -1
- package/src/IndexerState.res +10 -11
- package/src/IndexerState.res.mjs +14 -11
- package/src/IndexerState.resi +2 -5
- package/src/IndexingAddresses.res +108 -0
- package/src/IndexingAddresses.res.mjs +101 -0
- package/src/IndexingAddresses.resi +34 -0
- package/src/Internal.res +155 -46
- package/src/Internal.res.mjs +30 -2
- package/src/LoadLayer.res +5 -5
- package/src/LoadLayer.res.mjs +6 -6
- package/src/LogSelection.res +102 -165
- package/src/LogSelection.res.mjs +101 -116
- package/src/Main.res +71 -236
- package/src/Main.res.mjs +60 -145
- package/src/Metrics.res +74 -0
- package/src/Metrics.res.mjs +75 -0
- package/src/PgStorage.res +4 -4
- package/src/PgStorage.res.mjs +5 -5
- package/src/Prometheus.res +10 -74
- package/src/Prometheus.res.mjs +124 -186
- package/src/PruneStaleHistory.res +2 -2
- package/src/PruneStaleHistory.res.mjs +3 -3
- package/src/RawEvent.res +7 -2
- package/src/RawEvent.res.mjs +4 -4
- package/src/Rollback.res +3 -3
- package/src/Rollback.res.mjs +4 -4
- package/src/RollbackCommit.res +4 -1
- package/src/RollbackCommit.res.mjs +3 -2
- package/src/SimulateDeadInputTracker.res +85 -0
- package/src/SimulateDeadInputTracker.res.mjs +73 -0
- package/src/SimulateDeadInputTracker.resi +12 -0
- package/src/SimulateItems.res +91 -23
- package/src/SimulateItems.res.mjs +55 -32
- package/src/TestIndexer.res +54 -40
- package/src/TestIndexer.res.mjs +37 -30
- package/src/bindings/Performance.res +7 -0
- package/src/bindings/Performance.res.mjs +21 -0
- package/src/bindings/Performance.resi +7 -0
- package/src/bindings/Viem.res +0 -41
- package/src/bindings/Viem.res.mjs +1 -43
- package/src/sources/BlockStore.res +46 -0
- package/src/sources/BlockStore.res.mjs +24 -0
- package/src/sources/EventRouter.res +36 -33
- package/src/sources/EventRouter.res.mjs +13 -14
- package/src/sources/Evm.res +83 -72
- package/src/sources/Evm.res.mjs +53 -69
- package/src/sources/EvmChain.res +18 -20
- package/src/sources/EvmChain.res.mjs +15 -15
- package/src/sources/EvmRpcClient.res +74 -5
- package/src/sources/EvmRpcClient.res.mjs +13 -4
- package/src/sources/FieldMask.res +39 -0
- package/src/sources/FieldMask.res.mjs +42 -0
- package/src/sources/Fuel.res +9 -6
- package/src/sources/Fuel.res.mjs +5 -10
- package/src/sources/HyperFuelSource.res +46 -50
- package/src/sources/HyperFuelSource.res.mjs +64 -54
- package/src/sources/HyperSync.res +39 -7
- package/src/sources/HyperSync.res.mjs +57 -37
- package/src/sources/HyperSync.resi +12 -2
- package/src/sources/HyperSyncClient.res +22 -111
- package/src/sources/HyperSyncClient.res.mjs +2 -30
- package/src/sources/HyperSyncHeightStream.res +1 -8
- package/src/sources/HyperSyncHeightStream.res.mjs +0 -2
- package/src/sources/HyperSyncSource.res +94 -107
- package/src/sources/HyperSyncSource.res.mjs +82 -86
- package/src/sources/Rpc.res +15 -47
- package/src/sources/Rpc.res.mjs +25 -56
- package/src/sources/RpcSource.res +320 -467
- package/src/sources/RpcSource.res.mjs +268 -385
- package/src/sources/RpcWebSocketHeightStream.res +0 -5
- package/src/sources/RpcWebSocketHeightStream.res.mjs +0 -2
- package/src/sources/SimulateSource.res +10 -4
- package/src/sources/SimulateSource.res.mjs +16 -6
- package/src/sources/Source.res +34 -6
- package/src/sources/SourceManager.res +86 -27
- package/src/sources/SourceManager.res.mjs +84 -47
- package/src/sources/SourceManager.resi +12 -3
- package/src/sources/Svm.res +31 -16
- package/src/sources/Svm.res.mjs +36 -13
- package/src/sources/SvmHyperSyncClient.res +5 -32
- package/src/sources/SvmHyperSyncSource.res +159 -134
- package/src/sources/SvmHyperSyncSource.res.mjs +164 -129
- package/src/sources/TransactionStore.res +49 -0
- package/src/sources/TransactionStore.res.mjs +30 -0
- package/src/tui/Tui.res +13 -16
- package/src/tui/Tui.res.mjs +12 -14
- package/svm.schema.json +49 -37
- package/src/bindings/Hrtime.res +0 -58
- package/src/bindings/Hrtime.res.mjs +0 -90
- package/src/bindings/Hrtime.resi +0 -30
package/src/Env.res.mjs
CHANGED
|
@@ -19,10 +19,6 @@ let targetBufferSize = EnvSafe.get(envSafe, "ENVIO_INDEXING_MAX_BUFFER_SIZE", S$
|
|
|
19
19
|
|
|
20
20
|
let maxAddrInPartition = EnvSafe.get(envSafe, "MAX_PARTITION_SIZE", S$RescriptSchema.int, undefined, 5000, undefined, undefined);
|
|
21
21
|
|
|
22
|
-
let maxBackfillConcurrency = EnvSafe.get(envSafe, "ENVIO_MAX_BACKFILL_CONCURRENCY", S$RescriptSchema.int, undefined, 30, undefined, undefined);
|
|
23
|
-
|
|
24
|
-
let maxRealtimeConcurrency = EnvSafe.get(envSafe, "ENVIO_MAX_REALTIME_CONCURRENCY", S$RescriptSchema.int, undefined, 200, undefined, undefined);
|
|
25
|
-
|
|
26
22
|
let inMemoryObjectsTarget = EnvSafe.get(envSafe, "ENVIO_IN_MEMORY_OBJECTS_TARGET", S$RescriptSchema.int, undefined, 100000, undefined, undefined);
|
|
27
23
|
|
|
28
24
|
let serverPort = EnvSafe.get(envSafe, "ENVIO_INDEXER_PORT", S$RescriptSchema.port(S$RescriptSchema.int, undefined), undefined, EnvSafe.get(envSafe, "METRICS_PORT", S$RescriptSchema.port(S$RescriptSchema.int, undefined), undefined, 9898, undefined, undefined), undefined, undefined);
|
|
@@ -224,8 +220,6 @@ export {
|
|
|
224
220
|
updateSyncTimeOnRestart,
|
|
225
221
|
targetBufferSize,
|
|
226
222
|
maxAddrInPartition,
|
|
227
|
-
maxBackfillConcurrency,
|
|
228
|
-
maxRealtimeConcurrency,
|
|
229
223
|
inMemoryObjectsTarget,
|
|
230
224
|
serverPort,
|
|
231
225
|
tuiEnvVar,
|
package/src/Envio.res
CHANGED
|
@@ -48,6 +48,7 @@ type svmTokenBalance = {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
type svmTransaction = {
|
|
51
|
+
transactionIndex?: int,
|
|
51
52
|
signatures: array<string>,
|
|
52
53
|
feePayer?: SvmTypes.Pubkey.t,
|
|
53
54
|
success?: bool,
|
|
@@ -65,18 +66,21 @@ type svmLog = {
|
|
|
65
66
|
message: string,
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
/** Block context for a matched instruction. `
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
/** Block context for a matched instruction. `slot`/`hash` are always present;
|
|
70
|
+
`time` can still be absent — HyperSync/Solana may not report a block time for
|
|
71
|
+
every slot. Every other field is opt-in via `field_selection.block_fields`,
|
|
72
|
+
materialised from the per-chain block store at batch prep. */
|
|
71
73
|
type svmInstructionBlock = {
|
|
72
74
|
/** Slot this instruction's block was matched in. */
|
|
73
75
|
slot: int,
|
|
74
|
-
/** Unix block time (seconds).
|
|
75
|
-
|
|
76
|
-
time: int,
|
|
77
|
-
/** Block hash. Currently always empty — populated by the future
|
|
78
|
-
reorg-guard `queryBlockHash(slot)` route. */
|
|
76
|
+
/** Unix block time (seconds). */
|
|
77
|
+
time?: int,
|
|
79
78
|
hash: string,
|
|
79
|
+
/** Block height (distinct from slot). Absent when not selected via
|
|
80
|
+
`field_selection.block_fields`. */
|
|
81
|
+
height?: int,
|
|
82
|
+
parentSlot?: int,
|
|
83
|
+
parentHash?: string,
|
|
80
84
|
}
|
|
81
85
|
|
|
82
86
|
/** The per-instruction payload handlers receive as their `instruction`
|
|
@@ -104,13 +108,15 @@ type svmInstruction = {
|
|
|
104
108
|
d8?: string,
|
|
105
109
|
/** Borsh-decoded params view. See [[svmInstructionParams]]. */
|
|
106
110
|
params?: svmInstructionParams,
|
|
107
|
-
/** Parent transaction.
|
|
108
|
-
`
|
|
111
|
+
/** Parent transaction. Carries only the fields selected via
|
|
112
|
+
`field_selection.transaction_fields`; absent when none are selected. */
|
|
109
113
|
transaction?: svmTransaction,
|
|
110
114
|
/** Program log entries scoped to this instruction. Absent when the
|
|
111
115
|
per-instruction `include_logs` flag is `false`. */
|
|
112
116
|
logs?: array<svmLog>,
|
|
113
|
-
|
|
117
|
+
// Omitted on construction; materialised onto the payload at batch prep,
|
|
118
|
+
// before a handler ever reads it.
|
|
119
|
+
block?: svmInstructionBlock,
|
|
114
120
|
}
|
|
115
121
|
|
|
116
122
|
/** Arguments passed to handlers registered via `indexer.onInstruction`. */
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// The single process-wide mutable state record, stashed on `globalThis` so a
|
|
2
|
+
// duplicate envio module instance — e.g. when the CLI's `bin.mjs` resolves
|
|
3
|
+
// envio from one path but the user's handlers resolve it from
|
|
4
|
+
// `node_modules/envio` — shares one state. Without this, each copy keeps its
|
|
5
|
+
// own module-level state: registration would read an empty registry and the
|
|
6
|
+
// `indexer.chains` getters would silently fall back to static config values.
|
|
7
|
+
//
|
|
8
|
+
// Slots are opaque (`unknown`) so this module stays at the bottom of the
|
|
9
|
+
// dependency graph; each owning module casts its slot to the real type once
|
|
10
|
+
// (`HandlerRegister` for the registration slots, `Main` for the runtime
|
|
11
|
+
// slots, `RollbackCommit` for its callbacks).
|
|
12
|
+
//
|
|
13
|
+
// Version-gated: the slot shapes can evolve between envio versions, so the
|
|
14
|
+
// guard uses strict full-version equality. On mismatch we throw with a
|
|
15
|
+
// deduplication hint instead of silently mixing shapes across builds.
|
|
16
|
+
type t = {
|
|
17
|
+
version: string,
|
|
18
|
+
eventRegistrations: dict<unknown>,
|
|
19
|
+
mutable activeRegistration: option<unknown>,
|
|
20
|
+
preRegistered: array<unknown>,
|
|
21
|
+
rollbackCommitCallbacks: array<unknown>,
|
|
22
|
+
mutable indexerState: option<unknown>,
|
|
23
|
+
mutable persistence: option<unknown>,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Record type with `mutable` so assignment typechecks; ReScript keeps the
|
|
27
|
+
// field name verbatim in the generated JS so the globalThis slot is
|
|
28
|
+
// `__envioGlobal`.
|
|
29
|
+
type globalThis = {mutable __envioGlobal: Nullable.t<t>}
|
|
30
|
+
@val external globalThis: globalThis = "globalThis"
|
|
31
|
+
|
|
32
|
+
let value: t = {
|
|
33
|
+
let version = Utils.EnvioPackage.value.version
|
|
34
|
+
switch globalThis.__envioGlobal->Nullable.toOption {
|
|
35
|
+
| Some(existing) if existing.version === version => existing
|
|
36
|
+
| Some(existing) =>
|
|
37
|
+
JsError.throwWithMessage(
|
|
38
|
+
`Multiple incompatible envio versions loaded in the same process: ${existing.version} and ${version}. Deduplicate the 'envio' dependency in your project.`,
|
|
39
|
+
)
|
|
40
|
+
| None =>
|
|
41
|
+
let fresh = {
|
|
42
|
+
version,
|
|
43
|
+
eventRegistrations: Dict.make(),
|
|
44
|
+
activeRegistration: None,
|
|
45
|
+
preRegistered: [],
|
|
46
|
+
rollbackCommitCallbacks: [],
|
|
47
|
+
indexerState: None,
|
|
48
|
+
persistence: None,
|
|
49
|
+
}
|
|
50
|
+
globalThis.__envioGlobal = Nullable.make(fresh)
|
|
51
|
+
fresh
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Utils from "./Utils.res.mjs";
|
|
4
|
+
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
5
|
+
|
|
6
|
+
let version = Utils.EnvioPackage.value.version;
|
|
7
|
+
|
|
8
|
+
let existing = globalThis.__envioGlobal;
|
|
9
|
+
|
|
10
|
+
let value;
|
|
11
|
+
|
|
12
|
+
if (existing == null) {
|
|
13
|
+
let fresh = {
|
|
14
|
+
version: version,
|
|
15
|
+
eventRegistrations: {},
|
|
16
|
+
activeRegistration: undefined,
|
|
17
|
+
preRegistered: [],
|
|
18
|
+
rollbackCommitCallbacks: [],
|
|
19
|
+
indexerState: undefined,
|
|
20
|
+
persistence: undefined
|
|
21
|
+
};
|
|
22
|
+
globalThis.__envioGlobal = fresh;
|
|
23
|
+
value = fresh;
|
|
24
|
+
} else {
|
|
25
|
+
value = existing.version === version ? existing : Stdlib_JsError.throwWithMessage(`Multiple incompatible envio versions loaded in the same process: ` + existing.version + ` and ` + version + `. Deduplicate the 'envio' dependency in your project.`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
value,
|
|
30
|
+
}
|
|
31
|
+
/* existing Not a pure module */
|
|
@@ -243,9 +243,13 @@ let getTopicEncoder = (abiType: string): (unknown => EvmTypes.Hex.t) => {
|
|
|
243
243
|
} else {
|
|
244
244
|
switch abiType {
|
|
245
245
|
| "address" =>
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
246
|
+
// Lowercase before encoding so mixed-case (checksummed) user input
|
|
247
|
+
// still matches the lowercase hex topics returned by sources.
|
|
248
|
+
|
|
249
|
+
(
|
|
250
|
+
(value: string) =>
|
|
251
|
+
value->String.toLowerCase->Address.unsafeFromString->TopicFilter.fromAddress
|
|
252
|
+
)->(Utils.magic: (string => EvmTypes.Hex.t) => unknown => EvmTypes.Hex.t)
|
|
249
253
|
|
|
250
254
|
| "bool" =>
|
|
251
255
|
TopicFilter.fromBool->(Utils.magic: (bool => EvmTypes.Hex.t) => unknown => EvmTypes.Hex.t)
|
|
@@ -306,7 +310,14 @@ let resolveFieldSelection = (
|
|
|
306
310
|
| Some(fields) => Utils.Set.fromArray(fields)
|
|
307
311
|
| None => globalTransactionFieldsSet
|
|
308
312
|
}
|
|
309
|
-
(
|
|
313
|
+
// The base eventConfig stores these as a string set (field names match the
|
|
314
|
+
// typed variants at runtime).
|
|
315
|
+
(
|
|
316
|
+
selectedBlockFields,
|
|
317
|
+
selectedTransactionFields->(
|
|
318
|
+
Utils.magic: Utils.Set.t<Internal.evmTransactionField> => Utils.Set.t<string>
|
|
319
|
+
),
|
|
320
|
+
)
|
|
310
321
|
}
|
|
311
322
|
|
|
312
323
|
// ============== Client-side address filter ==============
|
|
@@ -319,28 +330,50 @@ let compileAddressFilter: string => (
|
|
|
319
330
|
return new Function("event", "blockNumber", "indexingAddresses", body);
|
|
320
331
|
}`)
|
|
321
332
|
|
|
322
|
-
// Body of the client-side address filter
|
|
323
|
-
//
|
|
324
|
-
// registered
|
|
325
|
-
//
|
|
326
|
-
//
|
|
327
|
-
|
|
328
|
-
|
|
333
|
+
// Body of the client-side address filter. Two analogous registered-at-or-before
|
|
334
|
+
// checks, ANDed: (1) for non-wildcard events, the log's srcAddress must itself be
|
|
335
|
+
// registered (ownership is resolved structurally by partition, but the temporal
|
|
336
|
+
// `effectiveStartBlock` gate lives here now); (2) a DNF of address-filtered param
|
|
337
|
+
// names (OR of AND-groups) for events that filter an indexed address param. The
|
|
338
|
+
// DNF is fixed here, so it's unrolled into one boolean expression — no per-event
|
|
339
|
+
// closure, loop, or array. `None` only for wildcard events without a param
|
|
340
|
+
// filter. Exposed for snapshotting.
|
|
341
|
+
// `srcAddressExpr` is the JS expression for the event's owning address: EVM and
|
|
342
|
+
// Fuel events expose `event.srcAddress`; SVM instructions expose `event.programId`.
|
|
343
|
+
let buildAddressFilterBody = (
|
|
344
|
+
groups: array<array<string>>,
|
|
345
|
+
~isWildcard: bool,
|
|
346
|
+
~srcAddressExpr: string="event.srcAddress",
|
|
347
|
+
): option<string> => {
|
|
348
|
+
let paramLeaf = name =>
|
|
349
|
+
`(ic = indexingAddresses[p[${JSON.stringify(
|
|
350
|
+
JSON.String(name),
|
|
351
|
+
)}]]) !== undefined && ic.effectiveStartBlock <= blockNumber`
|
|
352
|
+
let paramDnf = switch groups {
|
|
329
353
|
| [] => None
|
|
330
354
|
| _ =>
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
355
|
+
Some(
|
|
356
|
+
groups
|
|
357
|
+
->Array.map(group => "(" ++ group->Array.map(paramLeaf)->Array.join(" && ") ++ ")")
|
|
358
|
+
->Array.join(" || "),
|
|
359
|
+
)
|
|
360
|
+
}
|
|
361
|
+
let srcLeaf = `(ic = indexingAddresses[${srcAddressExpr}]) !== undefined && ic.effectiveStartBlock <= blockNumber`
|
|
362
|
+
switch (isWildcard, paramDnf) {
|
|
363
|
+
| (true, None) => None
|
|
364
|
+
| (true, Some(dnf)) => Some("var p = event.params, ic; return " ++ dnf ++ ";")
|
|
365
|
+
| (false, None) => Some("var ic; return " ++ srcLeaf ++ ";")
|
|
366
|
+
| (false, Some(dnf)) =>
|
|
367
|
+
Some("var p = event.params, ic; return " ++ srcLeaf ++ " && (" ++ dnf ++ ");")
|
|
338
368
|
}
|
|
339
369
|
}
|
|
340
370
|
|
|
341
|
-
let buildAddressFilter = (
|
|
342
|
-
|
|
343
|
-
|
|
371
|
+
let buildAddressFilter = (
|
|
372
|
+
groups: array<array<string>>,
|
|
373
|
+
~isWildcard: bool,
|
|
374
|
+
~srcAddressExpr: string="event.srcAddress",
|
|
375
|
+
): option<(Internal.eventPayload, int, dict<Internal.indexingContract>) => bool> =>
|
|
376
|
+
buildAddressFilterBody(groups, ~isWildcard, ~srcAddressExpr)->Option.map(compileAddressFilter)
|
|
344
377
|
|
|
345
378
|
// ============== Build complete EVM event config ==============
|
|
346
379
|
|
|
@@ -349,46 +382,12 @@ let buildEvmEventConfig = (
|
|
|
349
382
|
~eventName: string,
|
|
350
383
|
~sighash: string,
|
|
351
384
|
~params: array<paramMeta>,
|
|
352
|
-
~isWildcard: bool,
|
|
353
|
-
~handler: option<Internal.handler>,
|
|
354
|
-
~contractRegister: option<Internal.contractRegister>,
|
|
355
|
-
~eventFilters: option<JSON.t>,
|
|
356
|
-
~probeChainId: int,
|
|
357
|
-
~onEventBlockFilterSchema: S.t<option<unknown>>,
|
|
358
385
|
~blockFields: option<array<Internal.evmBlockField>>=?,
|
|
359
386
|
~transactionFields: option<array<Internal.evmTransactionField>>=?,
|
|
360
|
-
~startBlock: option<int>=?,
|
|
361
387
|
~globalBlockFieldsSet: Utils.Set.t<Internal.evmBlockField>=Utils.Set.make(),
|
|
362
388
|
~globalTransactionFieldsSet: Utils.Set.t<Internal.evmTransactionField>=Utils.Set.make(),
|
|
363
389
|
): Internal.evmEventConfig => {
|
|
364
390
|
let topicCount = params->Array.reduce(1, (acc, p) => p.indexed ? acc + 1 : acc)
|
|
365
|
-
let indexedParams = params->Array.filter(p => p.indexed)
|
|
366
|
-
|
|
367
|
-
let {
|
|
368
|
-
getEventFiltersOrThrow,
|
|
369
|
-
filterByAddresses,
|
|
370
|
-
addressFilterParamGroups,
|
|
371
|
-
startBlock: whereStartBlock,
|
|
372
|
-
} = LogSelection.parseEventFiltersOrThrow(
|
|
373
|
-
~eventFilters,
|
|
374
|
-
~sighash,
|
|
375
|
-
~params=indexedParams->Array.map(p => p.name),
|
|
376
|
-
~contractName,
|
|
377
|
-
~probeChainId,
|
|
378
|
-
~onEventBlockFilterSchema,
|
|
379
|
-
~topic1=?indexedParams->Array.get(0)->Option.map(buildTopicGetter),
|
|
380
|
-
~topic2=?indexedParams->Array.get(1)->Option.map(buildTopicGetter),
|
|
381
|
-
~topic3=?indexedParams->Array.get(2)->Option.map(buildTopicGetter),
|
|
382
|
-
)
|
|
383
|
-
|
|
384
|
-
// `where.block.number._gte` overrides the contract-level startBlock
|
|
385
|
-
// when present. The user opts into this explicitly in handler code so
|
|
386
|
-
// it should win over the `config.yaml` contract `start_block`; absent
|
|
387
|
-
// `where.block`, the caller's contract-level value passes through.
|
|
388
|
-
let resolvedStartBlock = switch whereStartBlock {
|
|
389
|
-
| Some(_) as sb => sb
|
|
390
|
-
| None => startBlock
|
|
391
|
-
}
|
|
392
391
|
|
|
393
392
|
let (selectedBlockFields, selectedTransactionFields) = resolveFieldSelection(
|
|
394
393
|
~blockFields,
|
|
@@ -401,25 +400,75 @@ let buildEvmEventConfig = (
|
|
|
401
400
|
id: sighash ++ "_" ++ topicCount->Int.toString,
|
|
402
401
|
name: eventName,
|
|
403
402
|
contractName,
|
|
404
|
-
isWildcard,
|
|
405
|
-
handler,
|
|
406
|
-
contractRegister,
|
|
407
403
|
paramsRawEventSchema: buildParamsSchema(params),
|
|
408
404
|
simulateParamsSchema: buildSimulateParamsSchema(params),
|
|
409
|
-
getEventFiltersOrThrow,
|
|
410
|
-
filterByAddresses,
|
|
411
|
-
clientAddressFilter: ?buildAddressFilter(addressFilterParamGroups),
|
|
412
|
-
dependsOnAddresses: !isWildcard || filterByAddresses,
|
|
413
|
-
startBlock: resolvedStartBlock,
|
|
414
405
|
selectedBlockFields,
|
|
415
406
|
selectedTransactionFields,
|
|
407
|
+
transactionFieldMask: Evm.eventTransactionFieldMask(selectedTransactionFields),
|
|
408
|
+
blockFieldMask: Evm.eventBlockFieldMask(
|
|
409
|
+
selectedBlockFields->(
|
|
410
|
+
Utils.magic: Utils.Set.t<Internal.evmBlockField> => Utils.Set.t<string>
|
|
411
|
+
),
|
|
412
|
+
),
|
|
416
413
|
sighash,
|
|
417
414
|
topicCount,
|
|
418
415
|
paramsMetadata: params,
|
|
419
416
|
}
|
|
420
417
|
}
|
|
421
418
|
|
|
422
|
-
//
|
|
419
|
+
// Enrich an EVM definition into a per-(event,chain) registration: resolve the
|
|
420
|
+
// registered `where` for this chain into `resolvedWhere` + address filters,
|
|
421
|
+
// and override `startBlock` with `where.block._gte`.
|
|
422
|
+
let buildEvmOnEventRegistration = (
|
|
423
|
+
~eventConfig: Internal.evmEventConfig,
|
|
424
|
+
~isWildcard: bool,
|
|
425
|
+
~handler: option<Internal.handler>,
|
|
426
|
+
~contractRegister: option<Internal.contractRegister>,
|
|
427
|
+
~where: option<JSON.t>,
|
|
428
|
+
~chainId: int,
|
|
429
|
+
~onEventBlockFilterSchema: S.t<option<unknown>>,
|
|
430
|
+
~startBlock: option<int>=?,
|
|
431
|
+
): Internal.evmOnEventRegistration => {
|
|
432
|
+
let indexedParams = eventConfig.paramsMetadata->Array.filter(p => p.indexed)
|
|
433
|
+
|
|
434
|
+
let {resolvedWhere, filterByAddresses, addressFilterParamGroups} = LogSelection.parseWhereOrThrow(
|
|
435
|
+
~where,
|
|
436
|
+
~sighash=eventConfig.sighash,
|
|
437
|
+
~params=indexedParams->Array.map(p => p.name),
|
|
438
|
+
~contractName=eventConfig.contractName,
|
|
439
|
+
~chainId,
|
|
440
|
+
~onEventBlockFilterSchema,
|
|
441
|
+
~topic1=?indexedParams->Array.get(0)->Option.map(buildTopicGetter),
|
|
442
|
+
~topic2=?indexedParams->Array.get(1)->Option.map(buildTopicGetter),
|
|
443
|
+
~topic3=?indexedParams->Array.get(2)->Option.map(buildTopicGetter),
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
// `where.block.number._gte` overrides the contract-level startBlock when
|
|
447
|
+
// present (an explicit per-event opt-in that wins over `config.yaml`);
|
|
448
|
+
// otherwise the contract/chain value passes through.
|
|
449
|
+
let resolvedStartBlock = switch resolvedWhere.startBlock {
|
|
450
|
+
| Some(_) as sb => sb
|
|
451
|
+
| None => startBlock
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
{
|
|
455
|
+
eventConfig: (eventConfig :> Internal.eventConfig),
|
|
456
|
+
isWildcard,
|
|
457
|
+
handler,
|
|
458
|
+
contractRegister,
|
|
459
|
+
resolvedWhere,
|
|
460
|
+
filterByAddresses,
|
|
461
|
+
clientAddressFilter: ?buildAddressFilter(addressFilterParamGroups, ~isWildcard),
|
|
462
|
+
dependsOnAddresses: Internal.dependsOnAddresses(~isWildcard, ~filterByAddresses),
|
|
463
|
+
startBlock: resolvedStartBlock,
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// ============== Build SVM instruction event config ==============
|
|
468
|
+
|
|
469
|
+
// Always-included block fields (slot, time, hash) are prepended at runtime so
|
|
470
|
+
// they're always present regardless of config.
|
|
471
|
+
let alwaysIncludedSvmBlockFields: array<Internal.svmBlockField> = [Slot, Time, Hash]
|
|
423
472
|
|
|
424
473
|
let buildSvmInstructionEventConfig = (
|
|
425
474
|
~contractName: string,
|
|
@@ -427,23 +476,32 @@ let buildSvmInstructionEventConfig = (
|
|
|
427
476
|
~programId: SvmTypes.Pubkey.t,
|
|
428
477
|
~discriminator: option<string>,
|
|
429
478
|
~discriminatorByteLen: int,
|
|
430
|
-
~includeTransaction: bool,
|
|
431
479
|
~includeLogs: bool,
|
|
432
|
-
~
|
|
480
|
+
~transactionFields: array<Internal.svmTransactionField>=[],
|
|
481
|
+
~blockFields: array<Internal.svmBlockField>=[],
|
|
433
482
|
~accountFilters: array<Internal.svmAccountFilterGroup>,
|
|
434
483
|
~isInner: option<bool>,
|
|
435
|
-
~isWildcard: bool,
|
|
436
|
-
~handler: option<Internal.handler>,
|
|
437
|
-
~contractRegister: option<Internal.contractRegister>,
|
|
438
484
|
~accounts: array<string>=[],
|
|
439
485
|
~args: JSON.t=JSON.Null,
|
|
440
486
|
~definedTypes: JSON.t=JSON.Null,
|
|
441
|
-
~startBlock: option<int>=?,
|
|
442
487
|
): Internal.svmInstructionEventConfig => {
|
|
443
488
|
let paramsSchema =
|
|
444
489
|
S.json(~validate=false)
|
|
445
490
|
->Utils.Schema.coerceToJsonPgType
|
|
446
491
|
->(Utils.magic: S.t<JSON.t> => S.t<Internal.eventParams>)
|
|
492
|
+
|
|
493
|
+
// The base eventConfig stores these as a string set (field names match the
|
|
494
|
+
// typed variants at runtime).
|
|
495
|
+
let selectedTransactionFields =
|
|
496
|
+
Utils.Set.fromArray(transactionFields)->(
|
|
497
|
+
Utils.magic: Utils.Set.t<Internal.svmTransactionField> => Utils.Set.t<string>
|
|
498
|
+
)
|
|
499
|
+
let selectedBlockFields = Utils.Set.fromArray(
|
|
500
|
+
Array.concat(alwaysIncludedSvmBlockFields, blockFields),
|
|
501
|
+
)
|
|
502
|
+
let blockFieldMask = Svm.eventBlockFieldMask(
|
|
503
|
+
selectedBlockFields->(Utils.magic: Utils.Set.t<Internal.svmBlockField> => Utils.Set.t<string>),
|
|
504
|
+
)
|
|
447
505
|
{
|
|
448
506
|
id: switch discriminator {
|
|
449
507
|
| Some(d) => d
|
|
@@ -451,20 +509,16 @@ let buildSvmInstructionEventConfig = (
|
|
|
451
509
|
},
|
|
452
510
|
name: instructionName,
|
|
453
511
|
contractName,
|
|
454
|
-
isWildcard,
|
|
455
|
-
handler,
|
|
456
|
-
contractRegister,
|
|
457
512
|
paramsRawEventSchema: paramsSchema,
|
|
458
513
|
simulateParamsSchema: paramsSchema,
|
|
459
|
-
filterByAddresses: false,
|
|
460
|
-
dependsOnAddresses: !isWildcard,
|
|
461
|
-
startBlock,
|
|
462
514
|
programId,
|
|
463
515
|
discriminator,
|
|
464
516
|
discriminatorByteLen,
|
|
465
|
-
includeTransaction,
|
|
466
517
|
includeLogs,
|
|
467
|
-
|
|
518
|
+
selectedTransactionFields,
|
|
519
|
+
transactionFieldMask: Svm.eventTransactionFieldMask(selectedTransactionFields),
|
|
520
|
+
selectedBlockFields,
|
|
521
|
+
blockFieldMask,
|
|
468
522
|
accountFilters,
|
|
469
523
|
isInner,
|
|
470
524
|
accounts,
|
|
@@ -473,16 +527,33 @@ let buildSvmInstructionEventConfig = (
|
|
|
473
527
|
}
|
|
474
528
|
}
|
|
475
529
|
|
|
530
|
+
// Enrich an SVM definition into a registration. SVM has no `where`; only the
|
|
531
|
+
// handler binding + wildcard-derived address gate are registration state.
|
|
532
|
+
let buildSvmOnEventRegistration = (
|
|
533
|
+
~eventConfig: Internal.svmInstructionEventConfig,
|
|
534
|
+
~isWildcard: bool,
|
|
535
|
+
~handler: option<Internal.handler>,
|
|
536
|
+
~contractRegister: option<Internal.contractRegister>,
|
|
537
|
+
~startBlock: option<int>=?,
|
|
538
|
+
): Internal.svmOnEventRegistration => {
|
|
539
|
+
eventConfig: (eventConfig :> Internal.eventConfig),
|
|
540
|
+
handler,
|
|
541
|
+
contractRegister,
|
|
542
|
+
isWildcard,
|
|
543
|
+
filterByAddresses: false,
|
|
544
|
+
dependsOnAddresses: Internal.dependsOnAddresses(~isWildcard, ~filterByAddresses=false),
|
|
545
|
+
clientAddressFilter: ?buildAddressFilter([], ~isWildcard, ~srcAddressExpr="event.programId"),
|
|
546
|
+
startBlock,
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// ============== Build Fuel event config ==============
|
|
550
|
+
|
|
476
551
|
let buildFuelEventConfig = (
|
|
477
552
|
~contractName: string,
|
|
478
553
|
~eventName: string,
|
|
479
554
|
~kind: string,
|
|
480
555
|
~sighash: string,
|
|
481
556
|
~rawAbi: JSON.t,
|
|
482
|
-
~isWildcard: bool,
|
|
483
|
-
~handler: option<Internal.handler>,
|
|
484
|
-
~contractRegister: option<Internal.contractRegister>,
|
|
485
|
-
~startBlock: option<int>=?,
|
|
486
557
|
): Internal.fuelEventConfig => {
|
|
487
558
|
let fuelKind = switch kind {
|
|
488
559
|
| "logData" =>
|
|
@@ -522,14 +593,32 @@ let buildFuelEventConfig = (
|
|
|
522
593
|
},
|
|
523
594
|
name: eventName,
|
|
524
595
|
contractName,
|
|
525
|
-
isWildcard,
|
|
526
|
-
handler,
|
|
527
|
-
contractRegister,
|
|
528
596
|
paramsRawEventSchema: paramsSchema,
|
|
529
597
|
simulateParamsSchema: paramsSchema,
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
598
|
+
// Fuel keeps the transaction and block inline on the payload; nothing to
|
|
599
|
+
// materialise.
|
|
600
|
+
selectedTransactionFields: Utils.Set.make(),
|
|
601
|
+
transactionFieldMask: 0.,
|
|
602
|
+
blockFieldMask: 0.,
|
|
533
603
|
kind: fuelKind,
|
|
534
604
|
}
|
|
535
605
|
}
|
|
606
|
+
|
|
607
|
+
// Enrich a Fuel definition into a registration (handler binding +
|
|
608
|
+
// wildcard-derived address gate; Fuel never filters by addresses).
|
|
609
|
+
let buildFuelOnEventRegistration = (
|
|
610
|
+
~eventConfig: Internal.fuelEventConfig,
|
|
611
|
+
~isWildcard: bool,
|
|
612
|
+
~handler: option<Internal.handler>,
|
|
613
|
+
~contractRegister: option<Internal.contractRegister>,
|
|
614
|
+
~startBlock: option<int>=?,
|
|
615
|
+
): Internal.fuelOnEventRegistration => {
|
|
616
|
+
eventConfig: (eventConfig :> Internal.eventConfig),
|
|
617
|
+
handler,
|
|
618
|
+
contractRegister,
|
|
619
|
+
isWildcard,
|
|
620
|
+
filterByAddresses: false,
|
|
621
|
+
dependsOnAddresses: Internal.dependsOnAddresses(~isWildcard, ~filterByAddresses=false),
|
|
622
|
+
clientAddressFilter: ?buildAddressFilter([], ~isWildcard),
|
|
623
|
+
startBlock,
|
|
624
|
+
}
|