envio 3.3.0-alpha.1 → 3.3.0-alpha.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/evm.schema.json +10 -0
  2. package/index.d.ts +45 -25
  3. package/licenses/CLA.md +35 -0
  4. package/licenses/EULA.md +67 -0
  5. package/licenses/LICENSE.md +45 -0
  6. package/licenses/README.md +35 -0
  7. package/package.json +10 -9
  8. package/src/Address.res +5 -2
  9. package/src/Address.res.mjs +3 -1
  10. package/src/Api.res +1 -1
  11. package/src/Api.res.mjs +1 -1
  12. package/src/Batch.res +0 -6
  13. package/src/Batch.res.mjs +1 -12
  14. package/src/BatchProcessing.res +7 -12
  15. package/src/BatchProcessing.res.mjs +8 -7
  16. package/src/Bin.res +3 -0
  17. package/src/Bin.res.mjs +4 -0
  18. package/src/ChainFetching.res +53 -16
  19. package/src/ChainFetching.res.mjs +37 -19
  20. package/src/ChainMetadata.res +1 -11
  21. package/src/ChainMetadata.res.mjs +1 -10
  22. package/src/ChainState.res +630 -161
  23. package/src/ChainState.res.mjs +438 -101
  24. package/src/ChainState.resi +77 -4
  25. package/src/Config.res +70 -38
  26. package/src/Config.res.mjs +52 -12
  27. package/src/ContractRegisterContext.res +2 -12
  28. package/src/ContractRegisterContext.res.mjs +3 -5
  29. package/src/Core.res +34 -3
  30. package/src/Core.res.mjs +12 -0
  31. package/src/CrossChainState.res +130 -89
  32. package/src/CrossChainState.res.mjs +66 -61
  33. package/src/CrossChainState.resi +1 -1
  34. package/src/Ecosystem.res +5 -6
  35. package/src/Ecosystem.res.mjs +3 -3
  36. package/src/Envio.res +17 -11
  37. package/src/EnvioGlobal.res +53 -0
  38. package/src/EnvioGlobal.res.mjs +31 -0
  39. package/src/EventConfigBuilder.res +191 -127
  40. package/src/EventConfigBuilder.res.mjs +116 -66
  41. package/src/EventProcessing.res +77 -28
  42. package/src/EventProcessing.res.mjs +56 -29
  43. package/src/ExitOnCaughtUp.res +10 -2
  44. package/src/ExitOnCaughtUp.res.mjs +10 -1
  45. package/src/FetchState.res +684 -415
  46. package/src/FetchState.res.mjs +505 -486
  47. package/src/HandlerLoader.res +8 -104
  48. package/src/HandlerLoader.res.mjs +2 -81
  49. package/src/HandlerRegister.res +520 -144
  50. package/src/HandlerRegister.res.mjs +286 -133
  51. package/src/HandlerRegister.resi +24 -8
  52. package/src/IndexerState.res +10 -3
  53. package/src/IndexerState.res.mjs +11 -4
  54. package/src/IndexerState.resi +2 -1
  55. package/src/IndexingAddresses.res +108 -0
  56. package/src/IndexingAddresses.res.mjs +101 -0
  57. package/src/IndexingAddresses.resi +34 -0
  58. package/src/Internal.res +166 -48
  59. package/src/Internal.res.mjs +30 -2
  60. package/src/LoadLayer.res +5 -5
  61. package/src/LoadLayer.res.mjs +6 -6
  62. package/src/LogSelection.res +92 -217
  63. package/src/LogSelection.res.mjs +95 -184
  64. package/src/Main.res +71 -236
  65. package/src/Main.res.mjs +60 -145
  66. package/src/Metrics.res +74 -0
  67. package/src/Metrics.res.mjs +75 -0
  68. package/src/PgStorage.res +4 -4
  69. package/src/PgStorage.res.mjs +5 -5
  70. package/src/Prometheus.res +10 -63
  71. package/src/Prometheus.res.mjs +124 -171
  72. package/src/PruneStaleHistory.res +2 -2
  73. package/src/PruneStaleHistory.res.mjs +3 -3
  74. package/src/RawEvent.res +7 -2
  75. package/src/RawEvent.res.mjs +4 -4
  76. package/src/Rollback.res +3 -3
  77. package/src/Rollback.res.mjs +4 -4
  78. package/src/RollbackCommit.res +4 -1
  79. package/src/RollbackCommit.res.mjs +3 -2
  80. package/src/SimulateDeadInputTracker.res +85 -0
  81. package/src/SimulateDeadInputTracker.res.mjs +73 -0
  82. package/src/SimulateDeadInputTracker.resi +12 -0
  83. package/src/SimulateItems.res +128 -28
  84. package/src/SimulateItems.res.mjs +82 -40
  85. package/src/TestIndexer.res +55 -41
  86. package/src/TestIndexer.res.mjs +39 -32
  87. package/src/TopicFilter.res +1 -25
  88. package/src/TopicFilter.res.mjs +0 -74
  89. package/src/bindings/Performance.res +7 -0
  90. package/src/bindings/Performance.res.mjs +21 -0
  91. package/src/bindings/Performance.resi +7 -0
  92. package/src/bindings/Viem.res +0 -46
  93. package/src/bindings/Viem.res.mjs +1 -43
  94. package/src/sources/BlockStore.res +46 -0
  95. package/src/sources/BlockStore.res.mjs +24 -0
  96. package/src/sources/EventRouter.res +31 -49
  97. package/src/sources/EventRouter.res.mjs +12 -25
  98. package/src/sources/Evm.res +83 -72
  99. package/src/sources/Evm.res.mjs +53 -69
  100. package/src/sources/EvmChain.res +7 -34
  101. package/src/sources/EvmChain.res.mjs +6 -27
  102. package/src/sources/EvmRpcClient.res +71 -5
  103. package/src/sources/EvmRpcClient.res.mjs +13 -4
  104. package/src/sources/FieldMask.res +39 -0
  105. package/src/sources/FieldMask.res.mjs +42 -0
  106. package/src/sources/Fuel.res +9 -6
  107. package/src/sources/Fuel.res.mjs +5 -10
  108. package/src/sources/HyperFuelSource.res +46 -50
  109. package/src/sources/HyperFuelSource.res.mjs +64 -54
  110. package/src/sources/HyperSync.res +45 -42
  111. package/src/sources/HyperSync.res.mjs +68 -60
  112. package/src/sources/HyperSync.resi +14 -4
  113. package/src/sources/HyperSyncClient.res +107 -119
  114. package/src/sources/HyperSyncClient.res.mjs +36 -31
  115. package/src/sources/HyperSyncHeightStream.res +1 -8
  116. package/src/sources/HyperSyncHeightStream.res.mjs +0 -2
  117. package/src/sources/HyperSyncSource.res +60 -254
  118. package/src/sources/HyperSyncSource.res.mjs +57 -180
  119. package/src/sources/Rpc.res +15 -79
  120. package/src/sources/Rpc.res.mjs +25 -101
  121. package/src/sources/RpcSource.res +221 -514
  122. package/src/sources/RpcSource.res.mjs +216 -411
  123. package/src/sources/RpcWebSocketHeightStream.res +0 -5
  124. package/src/sources/RpcWebSocketHeightStream.res.mjs +0 -2
  125. package/src/sources/SimulateSource.res +44 -20
  126. package/src/sources/SimulateSource.res.mjs +41 -14
  127. package/src/sources/Source.res +34 -6
  128. package/src/sources/SourceManager.res +77 -19
  129. package/src/sources/SourceManager.res.mjs +63 -21
  130. package/src/sources/SourceManager.resi +10 -0
  131. package/src/sources/Svm.res +31 -16
  132. package/src/sources/Svm.res.mjs +36 -13
  133. package/src/sources/SvmHyperSyncClient.res +5 -32
  134. package/src/sources/SvmHyperSyncSource.res +159 -134
  135. package/src/sources/SvmHyperSyncSource.res.mjs +164 -129
  136. package/src/sources/TransactionStore.res +49 -0
  137. package/src/sources/TransactionStore.res.mjs +30 -0
  138. package/src/tui/Tui.res +13 -16
  139. package/src/tui/Tui.res.mjs +12 -14
  140. package/svm.schema.json +49 -37
  141. package/src/bindings/Hrtime.res +0 -58
  142. package/src/bindings/Hrtime.res.mjs +0 -90
  143. package/src/bindings/Hrtime.resi +0 -30
@@ -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 */
@@ -236,54 +236,30 @@ let buildSimulateParamsSchema = (params: array<paramMeta>): S.t<Internal.eventPa
236
236
 
237
237
  // ============== Build topic filter getters ==============
238
238
 
239
- let getTopicEncoder = (abiType: string): (unknown => EvmTypes.Hex.t) => {
240
- // Handle array/tuple types - these get keccak256'd
241
- if abiType->String.endsWith("]") || abiType->String.startsWith("(") {
242
- TopicFilter.castToHexUnsafe->(Utils.magic: ('a => EvmTypes.Hex.t) => unknown => EvmTypes.Hex.t)
243
- } else {
244
- switch abiType {
245
- | "address" =>
246
- TopicFilter.fromAddress->(
247
- Utils.magic: (Address.t => EvmTypes.Hex.t) => unknown => EvmTypes.Hex.t
248
- )
249
-
250
- | "bool" =>
251
- TopicFilter.fromBool->(Utils.magic: (bool => EvmTypes.Hex.t) => unknown => EvmTypes.Hex.t)
252
- | "string" =>
253
- TopicFilter.fromDynamicString->(
254
- Utils.magic: (string => EvmTypes.Hex.t) => unknown => EvmTypes.Hex.t
255
- )
256
-
257
- | "bytes" =>
258
- TopicFilter.fromDynamicBytes->(
259
- Utils.magic: (string => EvmTypes.Hex.t) => unknown => EvmTypes.Hex.t
260
- )
261
-
262
- | t if t->String.startsWith("uint") =>
263
- TopicFilter.fromBigInt->(Utils.magic: (bigint => EvmTypes.Hex.t) => unknown => EvmTypes.Hex.t)
264
- | t if t->String.startsWith("int") =>
265
- TopicFilter.fromSignedBigInt->(
266
- Utils.magic: (bigint => EvmTypes.Hex.t) => unknown => EvmTypes.Hex.t
267
- )
268
-
269
- | t if t->String.startsWith("bytes") =>
270
- TopicFilter.castToHexUnsafe->(
271
- Utils.magic: ('a => EvmTypes.Hex.t) => unknown => EvmTypes.Hex.t
272
- )
273
-
274
- | other => JsError.throwWithMessage(`Unsupported topic filter ABI type: ${other}`)
275
- }
276
- }
277
- }
239
+ let getTopicEncoder = (abiType: string): (unknown => EvmTypes.Hex.t) => value =>
240
+ Core.getAddon().encodeIndexedTopic(~abiType, ~value)
278
241
 
279
242
  let buildTopicGetter = (p: paramMeta) => {
280
243
  let encoder = getTopicEncoder(p.abiType)
244
+ let isTuple = p.abiType->String.startsWith("(")
281
245
  (eventFilter: dict<JSON.t>) =>
282
246
  eventFilter
283
247
  ->Utils.Dict.dangerouslyGetNonOption(p.name)
284
- ->Option.mapOr([], topicFilters =>
285
- topicFilters->(Utils.magic: JSON.t => unknown)->normalizeOrThrow->Array.map(encoder)
286
- )
248
+ ->Option.mapOr([], topicFilters => {
249
+ let raw = topicFilters->(Utils.magic: JSON.t => unknown)
250
+ // A tuple filter value is itself an array, so a directly-passed tuple is
251
+ // indistinguishable from an OR-list by shape alone. A single tuple is
252
+ // the common case, so try it first; when the value doesn't ABI-encode as
253
+ // one tuple it must be an OR-list of tuples.
254
+ if isTuple {
255
+ switch encoder(raw) {
256
+ | encoded => [encoded]
257
+ | exception _ => raw->normalizeOrThrow->Array.map(encoder)
258
+ }
259
+ } else {
260
+ raw->normalizeOrThrow->Array.map(encoder)
261
+ }
262
+ })
287
263
  }
288
264
 
289
265
  // ============== Field selection ==============
@@ -306,7 +282,14 @@ let resolveFieldSelection = (
306
282
  | Some(fields) => Utils.Set.fromArray(fields)
307
283
  | None => globalTransactionFieldsSet
308
284
  }
309
- (selectedBlockFields, selectedTransactionFields)
285
+ // The base eventConfig stores these as a string set (field names match the
286
+ // typed variants at runtime).
287
+ (
288
+ selectedBlockFields,
289
+ selectedTransactionFields->(
290
+ Utils.magic: Utils.Set.t<Internal.evmTransactionField> => Utils.Set.t<string>
291
+ ),
292
+ )
310
293
  }
311
294
 
312
295
  // ============== Client-side address filter ==============
@@ -319,28 +302,50 @@ let compileAddressFilter: string => (
319
302
  return new Function("event", "blockNumber", "indexingAddresses", body);
320
303
  }`)
321
304
 
322
- // Body of the client-side address filter for a DNF of address-filtered param
323
- // names (OR of AND-groups): keep the event only if some group's params are all
324
- // registered at or before the log's block. The DNF is fixed here, so it's
325
- // unrolled into one boolean expression no per-event closure, loop, or array.
326
- // `None` when there's no address-param filter. Exposed for snapshotting.
327
- let buildAddressFilterBody = (groups: array<array<string>>): option<string> => {
328
- switch groups {
305
+ // Body of the client-side address filter. Two analogous registered-at-or-before
306
+ // checks, ANDed: (1) for non-wildcard events, the log's srcAddress must itself be
307
+ // registered (ownership is resolved structurally by partition, but the temporal
308
+ // `effectiveStartBlock` gate lives here now); (2) a DNF of address-filtered param
309
+ // names (OR of AND-groups) for events that filter an indexed address param. The
310
+ // DNF is fixed here, so it's unrolled into one boolean expression — no per-event
311
+ // closure, loop, or array. `None` only for wildcard events without a param
312
+ // filter. Exposed for snapshotting.
313
+ // `srcAddressExpr` is the JS expression for the event's owning address: EVM and
314
+ // Fuel events expose `event.srcAddress`; SVM instructions expose `event.programId`.
315
+ let buildAddressFilterBody = (
316
+ groups: array<array<string>>,
317
+ ~isWildcard: bool,
318
+ ~srcAddressExpr: string="event.srcAddress",
319
+ ): option<string> => {
320
+ let paramLeaf = name =>
321
+ `(ic = indexingAddresses[p[${JSON.stringify(
322
+ JSON.String(name),
323
+ )}]]) !== undefined && ic.effectiveStartBlock <= blockNumber`
324
+ let paramDnf = switch groups {
329
325
  | [] => None
330
326
  | _ =>
331
- let leaf = name =>
332
- `(ic = indexingAddresses[p[${JSON.stringify(
333
- JSON.String(name),
334
- )}]]) !== undefined && ic.effectiveStartBlock <= blockNumber`
335
- let groupExprs =
336
- groups->Array.map(group => "(" ++ group->Array.map(leaf)->Array.join(" && ") ++ ")")
337
- Some("var p = event.params, ic; return " ++ groupExprs->Array.join(" || ") ++ ";")
327
+ Some(
328
+ groups
329
+ ->Array.map(group => "(" ++ group->Array.map(paramLeaf)->Array.join(" && ") ++ ")")
330
+ ->Array.join(" || "),
331
+ )
332
+ }
333
+ let srcLeaf = `(ic = indexingAddresses[${srcAddressExpr}]) !== undefined && ic.effectiveStartBlock <= blockNumber`
334
+ switch (isWildcard, paramDnf) {
335
+ | (true, None) => None
336
+ | (true, Some(dnf)) => Some("var p = event.params, ic; return " ++ dnf ++ ";")
337
+ | (false, None) => Some("var ic; return " ++ srcLeaf ++ ";")
338
+ | (false, Some(dnf)) =>
339
+ Some("var p = event.params, ic; return " ++ srcLeaf ++ " && (" ++ dnf ++ ");")
338
340
  }
339
341
  }
340
342
 
341
- let buildAddressFilter = (groups: array<array<string>>): option<
342
- (Internal.eventPayload, int, dict<Internal.indexingContract>) => bool,
343
- > => buildAddressFilterBody(groups)->Option.map(compileAddressFilter)
343
+ let buildAddressFilter = (
344
+ groups: array<array<string>>,
345
+ ~isWildcard: bool,
346
+ ~srcAddressExpr: string="event.srcAddress",
347
+ ): option<(Internal.eventPayload, int, dict<Internal.indexingContract>) => bool> =>
348
+ buildAddressFilterBody(groups, ~isWildcard, ~srcAddressExpr)->Option.map(compileAddressFilter)
344
349
 
345
350
  // ============== Build complete EVM event config ==============
346
351
 
@@ -349,46 +354,12 @@ let buildEvmEventConfig = (
349
354
  ~eventName: string,
350
355
  ~sighash: string,
351
356
  ~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
357
  ~blockFields: option<array<Internal.evmBlockField>>=?,
359
358
  ~transactionFields: option<array<Internal.evmTransactionField>>=?,
360
- ~startBlock: option<int>=?,
361
359
  ~globalBlockFieldsSet: Utils.Set.t<Internal.evmBlockField>=Utils.Set.make(),
362
360
  ~globalTransactionFieldsSet: Utils.Set.t<Internal.evmTransactionField>=Utils.Set.make(),
363
361
  ): Internal.evmEventConfig => {
364
362
  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
363
 
393
364
  let (selectedBlockFields, selectedTransactionFields) = resolveFieldSelection(
394
365
  ~blockFields,
@@ -401,25 +372,76 @@ let buildEvmEventConfig = (
401
372
  id: sighash ++ "_" ++ topicCount->Int.toString,
402
373
  name: eventName,
403
374
  contractName,
404
- isWildcard,
405
- handler,
406
- contractRegister,
407
375
  paramsRawEventSchema: buildParamsSchema(params),
408
376
  simulateParamsSchema: buildSimulateParamsSchema(params),
409
- getEventFiltersOrThrow,
410
- filterByAddresses,
411
- clientAddressFilter: ?buildAddressFilter(addressFilterParamGroups),
412
- dependsOnAddresses: !isWildcard || filterByAddresses,
413
- startBlock: resolvedStartBlock,
414
377
  selectedBlockFields,
415
378
  selectedTransactionFields,
379
+ transactionFieldMask: Evm.eventTransactionFieldMask(selectedTransactionFields),
380
+ blockFieldMask: Evm.eventBlockFieldMask(
381
+ selectedBlockFields->(
382
+ Utils.magic: Utils.Set.t<Internal.evmBlockField> => Utils.Set.t<string>
383
+ ),
384
+ ),
416
385
  sighash,
417
386
  topicCount,
418
387
  paramsMetadata: params,
419
388
  }
420
389
  }
421
390
 
422
- // ============== Build Fuel event config ==============
391
+ // Enrich an EVM definition into a per-(event,chain) registration: resolve the
392
+ // registered `where` for this chain into `resolvedWhere` + address filters,
393
+ // and override `startBlock` with `where.block._gte`.
394
+ let buildEvmOnEventRegistration = (
395
+ ~eventConfig: Internal.evmEventConfig,
396
+ ~isWildcard: bool,
397
+ ~handler: option<Internal.handler>,
398
+ ~contractRegister: option<Internal.contractRegister>,
399
+ ~where: option<JSON.t>,
400
+ ~chainId: int,
401
+ ~onEventBlockFilterSchema: S.t<option<unknown>>,
402
+ ~startBlock: option<int>=?,
403
+ ): Internal.evmOnEventRegistration => {
404
+ let indexedParams = eventConfig.paramsMetadata->Array.filter(p => p.indexed)
405
+
406
+ let {resolvedWhere, filterByAddresses, addressFilterParamGroups} = LogSelection.parseWhereOrThrow(
407
+ ~where,
408
+ ~sighash=eventConfig.sighash,
409
+ ~params=indexedParams->Array.map(p => p.name),
410
+ ~contractName=eventConfig.contractName,
411
+ ~chainId,
412
+ ~onEventBlockFilterSchema,
413
+ ~topic1=?indexedParams->Array.get(0)->Option.map(buildTopicGetter),
414
+ ~topic2=?indexedParams->Array.get(1)->Option.map(buildTopicGetter),
415
+ ~topic3=?indexedParams->Array.get(2)->Option.map(buildTopicGetter),
416
+ )
417
+
418
+ // `where.block.number._gte` overrides the contract-level startBlock when
419
+ // present (an explicit per-event opt-in that wins over `config.yaml`);
420
+ // otherwise the contract/chain value passes through.
421
+ let resolvedStartBlock = switch resolvedWhere.startBlock {
422
+ | Some(_) as sb => sb
423
+ | None => startBlock
424
+ }
425
+
426
+ {
427
+ index: -1,
428
+ eventConfig: (eventConfig :> Internal.eventConfig),
429
+ isWildcard,
430
+ handler,
431
+ contractRegister,
432
+ resolvedWhere,
433
+ filterByAddresses,
434
+ clientAddressFilter: ?buildAddressFilter(addressFilterParamGroups, ~isWildcard),
435
+ dependsOnAddresses: Internal.dependsOnAddresses(~isWildcard, ~filterByAddresses),
436
+ startBlock: resolvedStartBlock,
437
+ }
438
+ }
439
+
440
+ // ============== Build SVM instruction event config ==============
441
+
442
+ // Always-included block fields (slot, time, hash) are prepended at runtime so
443
+ // they're always present regardless of config.
444
+ let alwaysIncludedSvmBlockFields: array<Internal.svmBlockField> = [Slot, Time, Hash]
423
445
 
424
446
  let buildSvmInstructionEventConfig = (
425
447
  ~contractName: string,
@@ -427,23 +449,32 @@ let buildSvmInstructionEventConfig = (
427
449
  ~programId: SvmTypes.Pubkey.t,
428
450
  ~discriminator: option<string>,
429
451
  ~discriminatorByteLen: int,
430
- ~includeTransaction: bool,
431
452
  ~includeLogs: bool,
432
- ~includeTokenBalances: bool,
453
+ ~transactionFields: array<Internal.svmTransactionField>=[],
454
+ ~blockFields: array<Internal.svmBlockField>=[],
433
455
  ~accountFilters: array<Internal.svmAccountFilterGroup>,
434
456
  ~isInner: option<bool>,
435
- ~isWildcard: bool,
436
- ~handler: option<Internal.handler>,
437
- ~contractRegister: option<Internal.contractRegister>,
438
457
  ~accounts: array<string>=[],
439
458
  ~args: JSON.t=JSON.Null,
440
459
  ~definedTypes: JSON.t=JSON.Null,
441
- ~startBlock: option<int>=?,
442
460
  ): Internal.svmInstructionEventConfig => {
443
461
  let paramsSchema =
444
462
  S.json(~validate=false)
445
463
  ->Utils.Schema.coerceToJsonPgType
446
464
  ->(Utils.magic: S.t<JSON.t> => S.t<Internal.eventParams>)
465
+
466
+ // The base eventConfig stores these as a string set (field names match the
467
+ // typed variants at runtime).
468
+ let selectedTransactionFields =
469
+ Utils.Set.fromArray(transactionFields)->(
470
+ Utils.magic: Utils.Set.t<Internal.svmTransactionField> => Utils.Set.t<string>
471
+ )
472
+ let selectedBlockFields = Utils.Set.fromArray(
473
+ Array.concat(alwaysIncludedSvmBlockFields, blockFields),
474
+ )
475
+ let blockFieldMask = Svm.eventBlockFieldMask(
476
+ selectedBlockFields->(Utils.magic: Utils.Set.t<Internal.svmBlockField> => Utils.Set.t<string>),
477
+ )
447
478
  {
448
479
  id: switch discriminator {
449
480
  | Some(d) => d
@@ -451,20 +482,16 @@ let buildSvmInstructionEventConfig = (
451
482
  },
452
483
  name: instructionName,
453
484
  contractName,
454
- isWildcard,
455
- handler,
456
- contractRegister,
457
485
  paramsRawEventSchema: paramsSchema,
458
486
  simulateParamsSchema: paramsSchema,
459
- filterByAddresses: false,
460
- dependsOnAddresses: !isWildcard,
461
- startBlock,
462
487
  programId,
463
488
  discriminator,
464
489
  discriminatorByteLen,
465
- includeTransaction,
466
490
  includeLogs,
467
- includeTokenBalances,
491
+ selectedTransactionFields,
492
+ transactionFieldMask: Svm.eventTransactionFieldMask(selectedTransactionFields),
493
+ selectedBlockFields,
494
+ blockFieldMask,
468
495
  accountFilters,
469
496
  isInner,
470
497
  accounts,
@@ -473,16 +500,34 @@ let buildSvmInstructionEventConfig = (
473
500
  }
474
501
  }
475
502
 
503
+ // Enrich an SVM definition into a registration. SVM has no `where`; only the
504
+ // handler binding + wildcard-derived address gate are registration state.
505
+ let buildSvmOnEventRegistration = (
506
+ ~eventConfig: Internal.svmInstructionEventConfig,
507
+ ~isWildcard: bool,
508
+ ~handler: option<Internal.handler>,
509
+ ~contractRegister: option<Internal.contractRegister>,
510
+ ~startBlock: option<int>=?,
511
+ ): Internal.svmOnEventRegistration => {
512
+ index: -1,
513
+ eventConfig: (eventConfig :> Internal.eventConfig),
514
+ handler,
515
+ contractRegister,
516
+ isWildcard,
517
+ filterByAddresses: false,
518
+ dependsOnAddresses: Internal.dependsOnAddresses(~isWildcard, ~filterByAddresses=false),
519
+ clientAddressFilter: ?buildAddressFilter([], ~isWildcard, ~srcAddressExpr="event.programId"),
520
+ startBlock,
521
+ }
522
+
523
+ // ============== Build Fuel event config ==============
524
+
476
525
  let buildFuelEventConfig = (
477
526
  ~contractName: string,
478
527
  ~eventName: string,
479
528
  ~kind: string,
480
529
  ~sighash: string,
481
530
  ~rawAbi: JSON.t,
482
- ~isWildcard: bool,
483
- ~handler: option<Internal.handler>,
484
- ~contractRegister: option<Internal.contractRegister>,
485
- ~startBlock: option<int>=?,
486
531
  ): Internal.fuelEventConfig => {
487
532
  let fuelKind = switch kind {
488
533
  | "logData" =>
@@ -522,14 +567,33 @@ let buildFuelEventConfig = (
522
567
  },
523
568
  name: eventName,
524
569
  contractName,
525
- isWildcard,
526
- handler,
527
- contractRegister,
528
570
  paramsRawEventSchema: paramsSchema,
529
571
  simulateParamsSchema: paramsSchema,
530
- filterByAddresses: false,
531
- dependsOnAddresses: !isWildcard,
532
- startBlock,
572
+ // Fuel keeps the transaction and block inline on the payload; nothing to
573
+ // materialise.
574
+ selectedTransactionFields: Utils.Set.make(),
575
+ transactionFieldMask: 0.,
576
+ blockFieldMask: 0.,
533
577
  kind: fuelKind,
534
578
  }
535
579
  }
580
+
581
+ // Enrich a Fuel definition into a registration (handler binding +
582
+ // wildcard-derived address gate; Fuel never filters by addresses).
583
+ let buildFuelOnEventRegistration = (
584
+ ~eventConfig: Internal.fuelEventConfig,
585
+ ~isWildcard: bool,
586
+ ~handler: option<Internal.handler>,
587
+ ~contractRegister: option<Internal.contractRegister>,
588
+ ~startBlock: option<int>=?,
589
+ ): Internal.fuelOnEventRegistration => {
590
+ index: -1,
591
+ eventConfig: (eventConfig :> Internal.eventConfig),
592
+ handler,
593
+ contractRegister,
594
+ isWildcard,
595
+ filterByAddresses: false,
596
+ dependsOnAddresses: Internal.dependsOnAddresses(~isWildcard, ~filterByAddresses=false),
597
+ clientAddressFilter: ?buildAddressFilter([], ~isWildcard),
598
+ startBlock,
599
+ }