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
@@ -44,7 +44,7 @@ let makeFromDbState: (
44
44
  ~config: Config.t,
45
45
  ~persistence: Persistence.t,
46
46
  ~initialState: Persistence.initialState,
47
- ~registrations: HandlerRegister.registrations,
47
+ ~registrationsByChainId: HandlerRegister.registrationsByChainId,
48
48
  ~isDevelopmentMode: bool=?,
49
49
  ~shouldUseTui: bool=?,
50
50
  ~exitAfterFirstEventBlock: bool=?,
@@ -107,6 +107,7 @@ let exitAfterFirstEventBlock: t => bool
107
107
  let isStopped: t => bool
108
108
  let epoch: t => int
109
109
  let pruneStaleEntityHistoryThrottler: t => Throttler.t
110
+ let simulateDeadInputTracker: t => option<SimulateDeadInputTracker.t>
110
111
 
111
112
  // Store domain operations.
112
113
  let queueProcessedBatch: (t, ~batch: Batch.t) => unit
@@ -0,0 +1,108 @@
1
+ type indexingAddress = Internal.indexingContract
2
+
3
+ type contractConfig = {startBlock: option<int>}
4
+
5
+ type t = dict<indexingAddress>
6
+
7
+ let deriveEffectiveStartBlock = (~registrationBlock: int, ~contractStartBlock: option<int>) => {
8
+ Pervasives.max(Pervasives.max(registrationBlock, 0), contractStartBlock->Option.getOr(0))
9
+ }
10
+
11
+ let makeContractConfigs = (~onEventRegistrations: array<Internal.onEventRegistration>): dict<
12
+ contractConfig,
13
+ > => {
14
+ let contractConfigs: dict<contractConfig> = Dict.make()
15
+ onEventRegistrations->Array.forEach(reg => {
16
+ let contractName = reg.eventConfig.contractName
17
+ switch contractConfigs->Utils.Dict.dangerouslyGetNonOption(contractName) {
18
+ | Some({startBlock}) =>
19
+ contractConfigs->Dict.set(
20
+ contractName,
21
+ {
22
+ startBlock: switch (startBlock, reg.startBlock) {
23
+ | (Some(a), Some(b)) => Some(Pervasives.min(a, b))
24
+ | (Some(_) as s, None) | (None, Some(_) as s) => s
25
+ | (None, None) => None
26
+ },
27
+ },
28
+ )
29
+ | None =>
30
+ contractConfigs->Dict.set(
31
+ contractName,
32
+ {
33
+ startBlock: reg.startBlock,
34
+ },
35
+ )
36
+ }
37
+ })
38
+ contractConfigs
39
+ }
40
+
41
+ let makeIndexingAddress = (
42
+ ~contract: Internal.indexingAddress,
43
+ ~contractConfigs: dict<contractConfig>,
44
+ ): indexingAddress => {
45
+ let contractStartBlock = switch contractConfigs->Utils.Dict.dangerouslyGetNonOption(
46
+ contract.contractName,
47
+ ) {
48
+ | Some({startBlock}) => startBlock
49
+ | None => None
50
+ }
51
+ {
52
+ address: contract.address,
53
+ contractName: contract.contractName,
54
+ registrationBlock: contract.registrationBlock,
55
+ effectiveStartBlock: deriveEffectiveStartBlock(
56
+ ~registrationBlock=contract.registrationBlock,
57
+ ~contractStartBlock,
58
+ ),
59
+ }
60
+ }
61
+
62
+ let make = (
63
+ ~contractConfigs: dict<contractConfig>,
64
+ ~addresses: array<Internal.indexingAddress>,
65
+ ): t => {
66
+ let indexingAddresses = Dict.make()
67
+ addresses->Array.forEach(contract => {
68
+ indexingAddresses->Dict.set(
69
+ contract.address->Address.toString,
70
+ makeIndexingAddress(~contract, ~contractConfigs),
71
+ )
72
+ })
73
+ indexingAddresses
74
+ }
75
+
76
+ let get = (indexingAddresses: t, address) =>
77
+ indexingAddresses->Utils.Dict.dangerouslyGetNonOption(address)
78
+
79
+ let size = (indexingAddresses: t) => indexingAddresses->Utils.Dict.size
80
+
81
+ let getContractAddresses = (indexingAddresses: t, ~contractName): array<Address.t> => {
82
+ let addresses = []
83
+ indexingAddresses->Utils.Dict.forEach(ia => {
84
+ if ia.contractName === contractName {
85
+ addresses->Array.push(ia.address)
86
+ }
87
+ })
88
+ addresses
89
+ }
90
+
91
+ // Underlying dict for the precompiled `clientAddressFilter` only — it does raw
92
+ // `indexingAddresses[srcAddress]` access in generated JS and can't take the opaque
93
+ // type. Don't reach for this elsewhere; use the domain accessors above.
94
+ let rawForFilter = (indexingAddresses: t): dict<indexingAddress> => indexingAddresses
95
+
96
+ let register = (indexingAddresses: t, additions: dict<indexingAddress>) => {
97
+ let _ = Utils.Dict.mergeInPlace(indexingAddresses, additions)
98
+ }
99
+
100
+ let rollbackInPlace = (indexingAddresses: t, ~targetBlockNumber: int): unit => {
101
+ // forEachWithKey is a `for..in`, so deleting the key currently being visited is
102
+ // safe — it doesn't affect enumeration of the remaining keys.
103
+ indexingAddresses->Utils.Dict.forEachWithKey((indexingContract, address) => {
104
+ if indexingContract.registrationBlock > targetBlockNumber {
105
+ indexingAddresses->Utils.Dict.deleteInPlace(address)
106
+ }
107
+ })
108
+ }
@@ -0,0 +1,101 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Utils from "./Utils.res.mjs";
4
+ import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
5
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
+
7
+ function deriveEffectiveStartBlock(registrationBlock, contractStartBlock) {
8
+ return Primitive_int.max(Primitive_int.max(registrationBlock, 0), Stdlib_Option.getOr(contractStartBlock, 0));
9
+ }
10
+
11
+ function makeContractConfigs(onEventRegistrations) {
12
+ let contractConfigs = {};
13
+ onEventRegistrations.forEach(reg => {
14
+ let contractName = reg.eventConfig.contractName;
15
+ let match = contractConfigs[contractName];
16
+ if (match !== undefined) {
17
+ let startBlock = match.startBlock;
18
+ let match$1 = reg.startBlock;
19
+ contractConfigs[contractName] = {
20
+ startBlock: startBlock !== undefined ? (
21
+ match$1 !== undefined ? Primitive_int.min(startBlock, match$1) : startBlock
22
+ ) : (
23
+ match$1 !== undefined ? match$1 : undefined
24
+ )
25
+ };
26
+ return;
27
+ }
28
+ contractConfigs[contractName] = {
29
+ startBlock: reg.startBlock
30
+ };
31
+ });
32
+ return contractConfigs;
33
+ }
34
+
35
+ function makeIndexingAddress(contract, contractConfigs) {
36
+ let match = contractConfigs[contract.contractName];
37
+ let contractStartBlock = match !== undefined ? match.startBlock : undefined;
38
+ return {
39
+ address: contract.address,
40
+ contractName: contract.contractName,
41
+ registrationBlock: contract.registrationBlock,
42
+ effectiveStartBlock: deriveEffectiveStartBlock(contract.registrationBlock, contractStartBlock)
43
+ };
44
+ }
45
+
46
+ function make(contractConfigs, addresses) {
47
+ let indexingAddresses = {};
48
+ addresses.forEach(contract => {
49
+ indexingAddresses[contract.address] = makeIndexingAddress(contract, contractConfigs);
50
+ });
51
+ return indexingAddresses;
52
+ }
53
+
54
+ function get(indexingAddresses, address) {
55
+ return indexingAddresses[address];
56
+ }
57
+
58
+ function size(indexingAddresses) {
59
+ return Utils.Dict.size(indexingAddresses);
60
+ }
61
+
62
+ function getContractAddresses(indexingAddresses, contractName) {
63
+ let addresses = [];
64
+ Utils.Dict.forEach(indexingAddresses, ia => {
65
+ if (ia.contractName === contractName) {
66
+ addresses.push(ia.address);
67
+ return;
68
+ }
69
+ });
70
+ return addresses;
71
+ }
72
+
73
+ function rawForFilter(indexingAddresses) {
74
+ return indexingAddresses;
75
+ }
76
+
77
+ function register(indexingAddresses, additions) {
78
+ Object.assign(indexingAddresses, additions);
79
+ }
80
+
81
+ function rollbackInPlace(indexingAddresses, targetBlockNumber) {
82
+ Utils.Dict.forEachWithKey(indexingAddresses, (indexingContract, address) => {
83
+ if (indexingContract.registrationBlock > targetBlockNumber) {
84
+ return Utils.Dict.deleteInPlace(indexingAddresses, address);
85
+ }
86
+ });
87
+ }
88
+
89
+ export {
90
+ deriveEffectiveStartBlock,
91
+ makeContractConfigs,
92
+ makeIndexingAddress,
93
+ make,
94
+ get,
95
+ size,
96
+ getContractAddresses,
97
+ rawForFilter,
98
+ register,
99
+ rollbackInPlace,
100
+ }
101
+ /* Utils Not a pure module */
@@ -0,0 +1,34 @@
1
+ type indexingAddress = Internal.indexingContract
2
+
3
+ type contractConfig = {startBlock: option<int>}
4
+
5
+ type t
6
+
7
+ let deriveEffectiveStartBlock: (~registrationBlock: int, ~contractStartBlock: option<int>) => int
8
+
9
+ let makeContractConfigs: (
10
+ ~onEventRegistrations: array<Internal.onEventRegistration>,
11
+ ) => dict<contractConfig>
12
+
13
+ let makeIndexingAddress: (
14
+ ~contract: Internal.indexingAddress,
15
+ ~contractConfigs: dict<contractConfig>,
16
+ ) => indexingAddress
17
+
18
+ let make: (~contractConfigs: dict<contractConfig>, ~addresses: array<Internal.indexingAddress>) => t
19
+
20
+ let get: (t, string) => option<indexingAddress>
21
+
22
+ let size: t => int
23
+
24
+ let getContractAddresses: (t, ~contractName: string) => array<Address.t>
25
+
26
+ // Underlying dict for the precompiled `clientAddressFilter` only, which does raw
27
+ // `indexingAddresses[srcAddress]` access in generated JS. Don't use elsewhere —
28
+ // prefer the domain accessors so the opaque type stays enforced.
29
+ let rawForFilter: t => dict<indexingAddress>
30
+
31
+ let register: (t, dict<indexingAddress>) => unit
32
+
33
+ // Deletes addresses registered after `targetBlockNumber` from the index in place.
34
+ let rollbackInPlace: (t, ~targetBlockNumber: int) => unit
package/src/Internal.res CHANGED
@@ -58,7 +58,6 @@ type evmTransactionField =
58
58
  | @as("root") Root
59
59
  | @as("status") Status
60
60
  | @as("yParity") YParity
61
- | @as("accessList") AccessList
62
61
  | @as("maxFeePerBlobGas") MaxFeePerBlobGas
63
62
  | @as("blobVersionedHashes") BlobVersionedHashes
64
63
  | @as("type") Type
@@ -67,6 +66,7 @@ type evmTransactionField =
67
66
  | @as("l1GasUsed") L1GasUsed
68
67
  | @as("l1FeeScalar") L1FeeScalar
69
68
  | @as("gasUsedForL1") GasUsedForL1
69
+ | @as("accessList") AccessList
70
70
  | @as("authorizationList") AuthorizationList
71
71
 
72
72
  let allEvmBlockFields: array<evmBlockField> = [
@@ -123,7 +123,6 @@ let allEvmTransactionFields: array<evmTransactionField> = [
123
123
  Root,
124
124
  Status,
125
125
  YParity,
126
- AccessList,
127
126
  MaxFeePerBlobGas,
128
127
  BlobVersionedHashes,
129
128
  Type,
@@ -132,10 +131,54 @@ let allEvmTransactionFields: array<evmTransactionField> = [
132
131
  L1GasUsed,
133
132
  L1FeeScalar,
134
133
  GasUsedForL1,
134
+ AccessList,
135
135
  AuthorizationList,
136
136
  ]
137
137
  let evmTransactionFieldSchema = S.enum(allEvmTransactionFields)
138
138
 
139
+ // SVM transaction fields. Order mirrors the Rust `SvmTxField` ordinals (the bit
140
+ // position in the selection mask) and `Svm.res` `transactionFields`.
141
+ type svmTransactionField =
142
+ | @as("transactionIndex") TransactionIndex
143
+ | @as("signatures") Signatures
144
+ | @as("feePayer") FeePayer
145
+ | @as("success") Success
146
+ | @as("err") Err
147
+ | @as("fee") Fee
148
+ | @as("computeUnitsConsumed") ComputeUnitsConsumed
149
+ | @as("accountKeys") AccountKeys
150
+ | @as("recentBlockhash") RecentBlockhash
151
+ | @as("version") Version
152
+ | @as("tokenBalances") TokenBalances
153
+
154
+ let allSvmTransactionFields: array<svmTransactionField> = [
155
+ TransactionIndex,
156
+ Signatures,
157
+ FeePayer,
158
+ Success,
159
+ Err,
160
+ Fee,
161
+ ComputeUnitsConsumed,
162
+ AccountKeys,
163
+ RecentBlockhash,
164
+ Version,
165
+ TokenBalances,
166
+ ]
167
+ let svmTransactionFieldSchema = S.enum(allSvmTransactionFields)
168
+
169
+ // All SVM block fields. `slot`/`time`/`hash` are always included; the rest are
170
+ // selectable via `field_selection.block_fields` (see `allSvmBlockFields`).
171
+ type svmBlockField =
172
+ | @as("slot") Slot
173
+ | @as("time") Time
174
+ | @as("hash") Hash
175
+ | @as("height") Height
176
+ | @as("parentSlot") ParentSlot
177
+ | @as("parentHash") ParentHash
178
+
179
+ let allSvmBlockFields: array<svmBlockField> = [Height, ParentSlot, ParentHash]
180
+ let svmBlockFieldSchema = S.enum(allSvmBlockFields)
181
+
139
182
  // Static sets of nullable field names — used by RpcSource and HyperSyncSource to wrap schemas with S.nullable
140
183
  let evmNullableBlockFields = Utils.Set.fromArray(
141
184
  (
@@ -265,8 +308,8 @@ type genericEvent<'params, 'block, 'transaction> = {
265
308
  block: 'block,
266
309
  }
267
310
 
268
- // Opaque internally — block-level values needed by the runtime
269
- // (blockNumber, timestamp, blockHash) live on the item instead.
311
+ // Opaque internally — the block number needed by the runtime lives on the
312
+ // item instead.
270
313
  type event
271
314
 
272
315
  // Opaque payload an item carries. A source builds an ecosystem-specific
@@ -277,7 +320,18 @@ type event
277
320
  // modules, not here, and are distinct per ecosystem.
278
321
  type eventPayload
279
322
 
280
- external payloadToEvent: eventPayload => event = "%identity"
323
+ // Generic access to the payload's `transaction`, written at batch prep for
324
+ // store-backed ecosystems (HyperSync) and present inline otherwise.
325
+ @get external getPayloadTransaction: eventPayload => Nullable.t<eventTransaction> = "transaction"
326
+ @set external setPayloadTransaction: (eventPayload, eventTransaction) => unit = "transaction"
327
+
328
+ // Generic access to the payload's `block`: written/enriched at batch prep for
329
+ // store-backed ecosystems (EVM/SVM HyperSync) and present inline otherwise.
330
+ @get external getPayloadBlock: eventPayload => Nullable.t<eventBlock> = "block"
331
+ @set external setPayloadBlock: (eventPayload, eventBlock) => unit = "block"
332
+
333
+ // The log's emitting address (EVM/Fuel; the program id carries it for SVM).
334
+ @get external getPayloadSrcAddress: eventPayload => Address.t = "srcAddress"
281
335
 
282
336
  type genericLoaderArgs<'event, 'context> = {
283
337
  event: 'event,
@@ -357,31 +411,33 @@ type indexingContract = {
357
411
  effectiveStartBlock: int,
358
412
  }
359
413
 
360
- // This is private so it's not manually constructed internally
361
- // The idea is that it can only be coerced from fuel/evmEventConfig
362
- // and it can include their fields. We prevent manual creation,
363
- // so the fields are not overwritten and we can safely cast the type back to fuel/evmEventConfig
414
+ // Definition of an event/instruction we know how to decode: identity + decode
415
+ // schemas + chain-independent field selection. A pure function of the ABI +
416
+ // config, shared across chains. `private` so it can only be coerced from an
417
+ // ecosystem variant (fields never overwritten), which lets sources cast the
418
+ // base back down to evm/fuel/svm safely.
364
419
  type eventConfig = private {
365
420
  id: string,
366
421
  name: string,
367
422
  contractName: string,
368
- isWildcard: bool,
369
- // Whether the event has an event filter which uses addresses
370
- filterByAddresses: bool,
371
- // Usually always false for wildcard events
372
- // But might be true for wildcard event with dynamic event filter by addresses
373
- dependsOnAddresses: bool,
374
- // Precompiled predicate (EVM only) for events that filter an indexed address
375
- // param by registered addresses. Given the decoded event and the log's block
376
- // number, drops an event whose param-address isn't registered at/before that
377
- // block — the param-level analogue of EventRouter's srcAddress
378
- // `effectiveStartBlock` check. Absent otherwise.
379
- clientAddressFilter?: (eventPayload, int, dict<indexingContract>) => bool,
380
- handler: option<handler>,
381
- contractRegister: option<contractRegister>,
382
423
  paramsRawEventSchema: S.schema<eventParams>,
383
424
  simulateParamsSchema: S.schema<eventParams>,
384
- startBlock: option<int>,
425
+ // Field names selected for the chain's transaction-store materialisation
426
+ // (camelCase, matching the ecosystem's `transactionFields`). Stored as a
427
+ // string set so the shared mask logic is ecosystem-agnostic; sources recover
428
+ // the typed view where they need it.
429
+ selectedTransactionFields: Utils.Set.t<string>,
430
+ // `selectedTransactionFields` precompiled to the transaction-store selection
431
+ // bitmask (bit per ecosystem field code). Materialisation reads this per item
432
+ // so each transaction decodes only the fields its event selected. `0.` when
433
+ // nothing is selected or the ecosystem carries the transaction inline (Fuel).
434
+ transactionFieldMask: float,
435
+ // Selected block fields precompiled to the block-store selection bitmask (bit
436
+ // per ecosystem field code). `0.` for ecosystems that carry the block fully
437
+ // inline (RPC/Fuel). The EVM selection always includes number/timestamp/hash,
438
+ // so an EVM mask always has their bits set; SVM stamps slot/time/hash inline
439
+ // from the response and its mask is the user's selection alone.
440
+ blockFieldMask: float,
385
441
  }
386
442
 
387
443
  type fuelEventKind =
@@ -406,30 +462,46 @@ type topicSelection = {
406
462
  topic3: array<EvmTypes.Hex.t>,
407
463
  }
408
464
 
465
+ // A single topic position of a resolved `where`: either static pre-encoded
466
+ // values, or a marker for "the currently registered addresses of this
467
+ // contract", expanded to topic values when a source query is built.
468
+ type topicFilter =
469
+ | Values(array<EvmTypes.Hex.t>)
470
+ | ContractAddresses({contractName: string})
471
+
472
+ type resolvedTopicSelection = {
473
+ topic0: array<EvmTypes.Hex.t>,
474
+ topic1: topicFilter,
475
+ topic2: topicFilter,
476
+ topic3: topicFilter,
477
+ }
478
+
479
+ // The registered `where` fully resolved at registration time for one chain.
480
+ // `topicSelections` is in disjunctive normal form (outer array is OR);
481
+ // an empty array means the `where` returned `false` for this chain.
482
+ type resolvedWhere = {
483
+ topicSelections: array<resolvedTopicSelection>,
484
+ startBlock: option<int>,
485
+ }
486
+
409
487
  // Per-event, per-invocation arguments passed to a `where` callback. The
410
488
  // concrete `chain` shape (which contract key it exposes) is generated per
411
489
  // event in user-project codegen — here it's an open record so codegen'd
412
490
  // types subtype-coerce into it cleanly.
413
491
  type onEventWhereArgs<'chain> = {chain: 'chain}
414
492
 
415
- type eventFilters =
416
- Static(array<topicSelection>) | Dynamic(array<Address.t> => array<topicSelection>)
417
-
418
493
  type evmEventConfig = {
419
494
  ...eventConfig,
420
- getEventFiltersOrThrow: ChainMap.Chain.t => eventFilters,
421
495
  selectedBlockFields: Utils.Set.t<evmBlockField>,
422
- selectedTransactionFields: Utils.Set.t<evmTransactionField>,
423
496
  sighash: string,
424
497
  topicCount: int,
425
498
  paramsMetadata: array<paramMeta>,
426
499
  }
427
500
 
428
- // Shared formula for `eventConfig.dependsOnAddresses`. Kept here so
429
- // `EventConfigBuilder.build{Evm,Fuel}EventConfig` and
430
- // `HandlerLoader.applyRegistrations` stay in sync when handler state flips
431
- // the value. Fuel events always have `filterByAddresses=false`, so callers
432
- // there simply pass it through as `false`.
501
+ // Shared formula for a registration's `dependsOnAddresses`. Kept here so the
502
+ // `EventConfigBuilder.build*OnEventRegistration` builders stay in sync. Fuel
503
+ // and SVM events always have `filterByAddresses=false`, so callers there pass
504
+ // it through as `false`.
433
505
  let dependsOnAddresses = (~isWildcard, ~filterByAddresses) => !isWildcard || filterByAddresses
434
506
 
435
507
  type evmContractConfig = {
@@ -448,6 +520,10 @@ type svmAccountFilterGroup = array<svmAccountFilter>
448
520
 
449
521
  type svmInstructionEventConfig = {
450
522
  ...eventConfig,
523
+ /** Block fields selected via `field_selection.block_fields` (`slot` is always
524
+ included and excluded from this set). Drives the block query columns;
525
+ precompiled to `blockFieldMask` for store materialisation. */
526
+ selectedBlockFields: Utils.Set.t<svmBlockField>,
451
527
  /** Base58 Solana program id this instruction belongs to. */
452
528
  programId: SvmTypes.Pubkey.t,
453
529
  /** Hex-encoded discriminator. `None` matches every instruction in the program. */
@@ -456,9 +532,7 @@ type svmInstructionEventConfig = {
456
532
  `dN` selector at query time and the dispatch-key precomputation in the
457
533
  router. */
458
534
  discriminatorByteLen: int,
459
- includeTransaction: bool,
460
535
  includeLogs: bool,
461
- includeTokenBalances: bool,
462
536
  /** Disjunctive normal form: outer array is OR of AND-groups, inner array is
463
537
  AND across positions. Empty outer array means "no account filter". */
464
538
  accountFilters: array<svmAccountFilterGroup>,
@@ -476,6 +550,49 @@ type svmInstructionEventConfig = {
476
550
  definedTypes: JSON.t,
477
551
  }
478
552
 
553
+ // Per-(event, chain) registration produced when user handler code registers an
554
+ // event (`onEvent`) or a dynamic contract registers. References its definition
555
+ // by value as `.eventConfig` and adds the handler binding plus the
556
+ // registration/`where`-derived fetch state. Not `private`: Fuel/SVM
557
+ // registrations add no ecosystem-specific fields (so they're bare aliases that
558
+ // must stay directly constructable), and the evm→base cast in sources is sound
559
+ // by ecosystem homogeneity — an EVM chain only ever holds `evmOnEventRegistration`s.
560
+ type onEventRegistration = {
561
+ // Chain-scoped sequential index — the registration's position in the
562
+ // chain's onEventRegistrations array, assigned when registration finishes
563
+ // (-1 until then). Native-routed items reference their registration by this
564
+ // index across the napi boundary; sources resolve it before creating an item.
565
+ index: int,
566
+ eventConfig: eventConfig,
567
+ handler: option<handler>,
568
+ contractRegister: option<contractRegister>,
569
+ isWildcard: bool,
570
+ // Whether the event has an event filter which uses addresses.
571
+ filterByAddresses: bool,
572
+ // Usually always false for wildcard events, but might be true for a wildcard
573
+ // event with a dynamic event filter by addresses.
574
+ dependsOnAddresses: bool,
575
+ // Precompiled predicate for events that filter an indexed address param by
576
+ // registered addresses (see `EventConfigBuilder.buildAddressFilter`); drops a
577
+ // decoded event whose param-address isn't registered at/before the log's
578
+ // block. Absent otherwise.
579
+ clientAddressFilter?: (eventPayload, int, dict<indexingContract>) => bool,
580
+ // Final start block: the contract/chain config value, overridden by a
581
+ // `where.block.number._gte` when the registered `where` supplies one.
582
+ startBlock: option<int>,
583
+ }
584
+
585
+ type evmOnEventRegistration = {
586
+ ...onEventRegistration,
587
+ resolvedWhere: resolvedWhere,
588
+ }
589
+
590
+ // Fuel and SVM registrations add no ecosystem-specific fetch state (their
591
+ // filters are config-derived and live on the definition), so they're bare
592
+ // aliases of the base registration.
593
+ type fuelOnEventRegistration = onEventRegistration
594
+ type svmOnEventRegistration = onEventRegistration
595
+
479
596
  type svmProgramConfig = {
480
597
  name: string,
481
598
  programId: SvmTypes.Pubkey.t,
@@ -492,16 +609,18 @@ type indexingAddress = {
492
609
 
493
610
  type dcs = array<indexingAddress>
494
611
 
495
- // Duplicate the type from item
496
- // to make item properly unboxed
612
+ // Duplicate the type from item to keep item properly unboxed. Runtime event
613
+ // items carry the registration their source already resolved from the
614
+ // ChainState-owned registration array.
497
615
  type eventItem = private {
498
616
  kind: [#0],
499
- eventConfig: eventConfig,
500
- timestamp: int,
617
+ onEventRegistration: onEventRegistration,
501
618
  chain: ChainMap.Chain.t,
502
619
  blockNumber: int,
503
- blockHash: string,
504
620
  logIndex: int,
621
+ // Within-block transaction index — the key into the per-chain transaction
622
+ // store. Unused (0) for ecosystems that carry the transaction inline (Fuel).
623
+ transactionIndex: int,
505
624
  payload: eventPayload,
506
625
  }
507
626
 
@@ -532,7 +651,7 @@ type onBlockArgs = {
532
651
  context: handlerContext,
533
652
  }
534
653
 
535
- type onBlockConfig = {
654
+ type onBlockRegistration = {
536
655
  // When there are multiple onBlock handlers per chain,
537
656
  // we want to use the order they are defined for sorting
538
657
  index: int,
@@ -548,15 +667,14 @@ type onBlockConfig = {
548
667
  type item =
549
668
  | @as(0)
550
669
  Event({
551
- eventConfig: eventConfig,
552
- timestamp: int,
670
+ onEventRegistration: onEventRegistration,
553
671
  chain: ChainMap.Chain.t,
554
672
  blockNumber: int,
555
- blockHash: string,
556
673
  logIndex: int,
674
+ transactionIndex: int,
557
675
  payload: eventPayload,
558
676
  })
559
- | @as(1) Block({onBlockConfig: onBlockConfig, blockNumber: int, logIndex: int})
677
+ | @as(1) Block({onBlockRegistration: onBlockRegistration, blockNumber: int, logIndex: int})
560
678
 
561
679
  external castUnsafeEventItem: item => eventItem = "%identity"
562
680
 
@@ -568,7 +686,7 @@ external getItemLogIndex: item => int = "logIndex"
568
686
  let getItemChainId = item =>
569
687
  switch item {
570
688
  | Event({chain}) => chain->ChainMap.Chain.toChainId
571
- | Block({onBlockConfig: {chainId}}) => chainId
689
+ | Block({onBlockRegistration: {chainId}}) => chainId
572
690
  }
573
691
 
574
692
  @get
@@ -653,7 +771,7 @@ type effect = {
653
771
  input: S.t<effectInput>,
654
772
  // The number of functions that are currently running.
655
773
  mutable activeCallsCount: int,
656
- mutable prevCallStartTimerRef: Hrtime.timeRef,
774
+ mutable prevCallStartTimerRef: Performance.timeRef,
657
775
  rateLimit: option<rateLimitState>,
658
776
  }
659
777
  let cacheTablePrefix = "envio_effect_"
@@ -60,7 +60,6 @@ let allEvmTransactionFields = [
60
60
  "root",
61
61
  "status",
62
62
  "yParity",
63
- "accessList",
64
63
  "maxFeePerBlobGas",
65
64
  "blobVersionedHashes",
66
65
  "type",
@@ -69,11 +68,36 @@ let allEvmTransactionFields = [
69
68
  "l1GasUsed",
70
69
  "l1FeeScalar",
71
70
  "gasUsedForL1",
71
+ "accessList",
72
72
  "authorizationList"
73
73
  ];
74
74
 
75
75
  let evmTransactionFieldSchema = S$RescriptSchema.$$enum(allEvmTransactionFields);
76
76
 
77
+ let allSvmTransactionFields = [
78
+ "transactionIndex",
79
+ "signatures",
80
+ "feePayer",
81
+ "success",
82
+ "err",
83
+ "fee",
84
+ "computeUnitsConsumed",
85
+ "accountKeys",
86
+ "recentBlockhash",
87
+ "version",
88
+ "tokenBalances"
89
+ ];
90
+
91
+ let svmTransactionFieldSchema = S$RescriptSchema.$$enum(allSvmTransactionFields);
92
+
93
+ let allSvmBlockFields = [
94
+ "height",
95
+ "parentSlot",
96
+ "parentHash"
97
+ ];
98
+
99
+ let svmBlockFieldSchema = S$RescriptSchema.$$enum(allSvmBlockFields);
100
+
77
101
  let evmNullableBlockFields = new Set([
78
102
  "nonce",
79
103
  "difficulty",
@@ -125,7 +149,7 @@ function getItemChainId(item) {
125
149
  if (item.kind === 0) {
126
150
  return item.chain;
127
151
  } else {
128
- return item.onBlockConfig.chainId;
152
+ return item.onBlockRegistration.chainId;
129
153
  }
130
154
  }
131
155
 
@@ -160,6 +184,10 @@ export {
160
184
  evmBlockFieldSchema,
161
185
  allEvmTransactionFields,
162
186
  evmTransactionFieldSchema,
187
+ allSvmTransactionFields,
188
+ svmTransactionFieldSchema,
189
+ allSvmBlockFields,
190
+ svmBlockFieldSchema,
163
191
  evmNullableBlockFields,
164
192
  evmNullableTransactionFields,
165
193
  dependsOnAddresses,