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
@@ -1,44 +1,2 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
-
3
- import * as Viem from "viem";
4
- import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
5
- import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
6
-
7
- let ParseError = /* @__PURE__ */Primitive_exceptions.create("Viem.ParseError");
8
-
9
- let UnknownContractName = /* @__PURE__ */Primitive_exceptions.create("Viem.UnknownContractName");
10
-
11
- function parseLogOrThrow(contractNameAbiMapping, contractName, topics, data) {
12
- let abi = contractNameAbiMapping[contractName];
13
- if (abi !== undefined) {
14
- let viemLog_abi = Primitive_option.valFromOption(abi);
15
- let viemLog = {
16
- abi: viemLog_abi,
17
- data: data,
18
- topics: topics
19
- };
20
- try {
21
- return Viem.decodeEventLog(viemLog);
22
- } catch (raw_exn) {
23
- let exn = Primitive_exceptions.internalToException(raw_exn);
24
- throw {
25
- RE_EXN_ID: ParseError,
26
- _1: exn,
27
- Error: new Error()
28
- };
29
- }
30
- } else {
31
- throw {
32
- RE_EXN_ID: UnknownContractName,
33
- contractName: contractName,
34
- Error: new Error()
35
- };
36
- }
37
- }
38
-
39
- export {
40
- ParseError,
41
- UnknownContractName,
42
- parseLogOrThrow,
43
- }
44
- /* viem Not a pure module */
2
+ /* This output is empty. Its source's type definitions, externals and/or unused code got optimized away. */
@@ -0,0 +1,46 @@
1
+ // Binding to the Rust `BlockStore` napi class. Blocks are kept in Rust as raw
2
+ // structs (their large fields never enter JS until read) keyed by block number.
3
+ // One store lives per chain on `ChainState`; each fetch response contributes a
4
+ // page that is merged in. At batch preparation the selected fields are
5
+ // materialised in bulk, off the JS thread, in columnar form and zipped into
6
+ // plain JS objects on the main thread.
7
+ type t
8
+
9
+ @send external newEvm: (Core.blockStoreCtor, ~shouldChecksum: bool) => t = "newEvm"
10
+ @send external newSvm: Core.blockStoreCtor => t = "newSvm"
11
+ @send external newFuel: Core.blockStoreCtor => t = "newFuel"
12
+
13
+ // The store's ecosystem is fixed here, from the chain's config. EVM carries the
14
+ // chain's address-checksumming setting; SVM/Fuel need no extra data.
15
+ let make = (~ecosystem: Ecosystem.name, ~shouldChecksum: bool): t => {
16
+ let ctor = Core.getAddon().blockStore
17
+ switch ecosystem {
18
+ | Evm => ctor->newEvm(~shouldChecksum)
19
+ | Svm => ctor->newSvm
20
+ | Fuel => ctor->newFuel
21
+ }
22
+ }
23
+
24
+ // One event's selected block fields → store selection bitmask, built from the
25
+ // ecosystem's ordered field-name array (the bit index is the field code shared
26
+ // with the Rust store, `EvmBlockField`).
27
+ let makeMaskFn = FieldMask.makeMaskFn
28
+
29
+ // Drain another store (a fetch-response page) into this one.
30
+ @send external merge: (t, t) => unit = "merge"
31
+
32
+ // Bulk-materialise blocks off the JS thread, one row per `blockNumbers[i]` key,
33
+ // decoding only the fields set in that row's own `masks[i]`. Result is aligned
34
+ // with the input.
35
+ @send
36
+ external materialize: (
37
+ t,
38
+ ~blockNumbers: array<int>,
39
+ ~masks: array<float>,
40
+ ) => promise<array<Internal.eventBlock>> = "materialize"
41
+
42
+ // Drop blocks at or below the given block (already processed).
43
+ @send external prune: (t, int) => unit = "prune"
44
+
45
+ // Drop blocks above the given block (rolled back).
46
+ @send external rollback: (t, int) => unit = "rollback"
@@ -0,0 +1,24 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Core from "../Core.res.mjs";
4
+ import * as FieldMask from "./FieldMask.res.mjs";
5
+
6
+ function make(ecosystem, shouldChecksum) {
7
+ let ctor = Core.getAddon().BlockStore;
8
+ switch (ecosystem) {
9
+ case "evm" :
10
+ return ctor.newEvm(shouldChecksum);
11
+ case "fuel" :
12
+ return ctor.newFuel();
13
+ case "svm" :
14
+ return ctor.newSvm();
15
+ }
16
+ }
17
+
18
+ let makeMaskFn = FieldMask.makeMaskFn;
19
+
20
+ export {
21
+ make,
22
+ makeMaskFn,
23
+ }
24
+ /* Core Not a pure module */
@@ -28,29 +28,25 @@ module Group = {
28
28
  }
29
29
  }
30
30
 
31
- let get = (
32
- group: t<'a>,
33
- ~contractAddress,
34
- ~blockNumber,
35
- ~indexingAddresses: dict<FetchState.indexingAddress>,
36
- ) =>
31
+ // Ownership only: resolve the owning contract from the partition's reverse
32
+ // index (the partition that fetched the log), not a chain-wide snapshot. The
33
+ // `effectiveStartBlock` temporal gate now lives in `clientAddressFilter`. The
34
+ // wildcard partition has an empty index → every log falls back to `wildcard`,
35
+ // so it can never claim an address-bound contract's logs.
36
+ let get = (group: t<'a>, ~contractAddress, ~contractNameByAddress: dict<string>) =>
37
37
  switch group {
38
38
  | {wildcard, byContractName} =>
39
- switch indexingAddresses->Utils.Dict.dangerouslyGetNonOption(
39
+ switch contractNameByAddress->Utils.Dict.dangerouslyGetNonOption(
40
40
  contractAddress->Address.toString,
41
41
  ) {
42
- | Some(indexingContract) =>
43
- if indexingContract.effectiveStartBlock <= blockNumber {
44
- switch byContractName->Utils.Dict.dangerouslyGetNonOption(indexingContract.contractName) {
45
- // Fall back to the wildcard handler when the indexed contract has no
46
- // matching event for this tag. This covers addresses registered for
47
- // contracts without events (persisted for future config changes) as
48
- // well as addresses whose contract has other events but not this one.
49
- | None => wildcard
50
- | Some(_) as event => event
51
- }
52
- } else {
53
- None
42
+ | Some(contractName) =>
43
+ switch byContractName->Utils.Dict.dangerouslyGetNonOption(contractName) {
44
+ // Fall back to the wildcard handler when the owning contract has no
45
+ // matching event for this tag. This covers addresses registered for
46
+ // contracts without events (persisted for future config changes) as
47
+ // well as addresses whose contract has other events but not this one.
48
+ | None => wildcard
49
+ | Some(_) as event => event
54
50
  }
55
51
  | None => wildcard
56
52
  }
@@ -89,34 +85,13 @@ let addOrThrow = (
89
85
  }
90
86
  }
91
87
 
92
- let get = (router: t<'a>, ~tag, ~contractAddress, ~blockNumber, ~indexingAddresses) => {
88
+ let get = (router: t<'a>, ~tag, ~contractAddress, ~contractNameByAddress) => {
93
89
  switch router->Utils.Dict.dangerouslyGetNonOption(tag) {
94
90
  | None => None
95
- | Some(group) => group->Group.get(~contractAddress, ~blockNumber, ~indexingAddresses)
91
+ | Some(group) => group->Group.get(~contractAddress, ~contractNameByAddress)
96
92
  }
97
93
  }
98
94
 
99
- let getEvmEventId = (~sighash, ~topicCount) => {
100
- sighash ++ "_" ++ topicCount->Int.toString
101
- }
102
-
103
- let fromEvmEventModsOrThrow = (events: array<Internal.evmEventConfig>, ~chain): t<
104
- Internal.evmEventConfig,
105
- > => {
106
- let router = empty()
107
- events->Array.forEach(config => {
108
- router->addOrThrow(
109
- config.id,
110
- config,
111
- ~contractName=config.contractName,
112
- ~eventName=config.name,
113
- ~chain,
114
- ~isWildcard=config.isWildcard,
115
- )
116
- })
117
- router
118
- }
119
-
120
95
  /** Dispatch key for SVM instructions. `None` matches any instruction in the
121
96
  program (lowest priority). */
122
97
  let getSvmEventId = (~programId: SvmTypes.Pubkey.t, ~discriminator: option<string>) =>
@@ -135,21 +110,26 @@ type svmProgramOrdering = {
135
110
  byteLengthsDesc: array<int>,
136
111
  }
137
112
 
138
- let fromSvmEventConfigsOrThrow = (events: array<Internal.svmInstructionEventConfig>, ~chain): (
139
- t<Internal.svmInstructionEventConfig>,
113
+ let fromSvmEventConfigsOrThrow = (events: array<Internal.svmOnEventRegistration>, ~chain): (
114
+ t<Internal.svmOnEventRegistration>,
140
115
  array<svmProgramOrdering>,
141
116
  ) => {
142
117
  let router = empty()
143
118
  events->Array.forEach(config => {
119
+ let eventConfig =
120
+ config.eventConfig->(Utils.magic: Internal.eventConfig => Internal.svmInstructionEventConfig)
144
121
  // The router tag must include the programId so two programs declaring the
145
122
  // same discriminator coexist. The source-side lookup uses the same shape
146
123
  // via `getSvmEventId(~programId, ~discriminator)`.
147
- let routerTag = getSvmEventId(~programId=config.programId, ~discriminator=config.discriminator)
124
+ let routerTag = getSvmEventId(
125
+ ~programId=eventConfig.programId,
126
+ ~discriminator=eventConfig.discriminator,
127
+ )
148
128
  router->addOrThrow(
149
129
  routerTag,
150
130
  config,
151
- ~contractName=config.contractName,
152
- ~eventName=config.name,
131
+ ~contractName=eventConfig.contractName,
132
+ ~eventName=eventConfig.name,
153
133
  ~chain,
154
134
  ~isWildcard=config.isWildcard,
155
135
  )
@@ -158,7 +138,9 @@ let fromSvmEventConfigsOrThrow = (events: array<Internal.svmInstructionEventConf
158
138
  // Per-program list of declared discriminator byte lengths, sorted desc.
159
139
  let byProgram: dict<Utils.Set.t<int>> = Dict.make()
160
140
  events->Array.forEach(config => {
161
- let key = config.programId->SvmTypes.Pubkey.toString
141
+ let eventConfig =
142
+ config.eventConfig->(Utils.magic: Internal.eventConfig => Internal.svmInstructionEventConfig)
143
+ let key = eventConfig.programId->SvmTypes.Pubkey.toString
162
144
  let set = switch byProgram->Utils.Dict.dangerouslyGetNonOption(key) {
163
145
  | Some(s) => s
164
146
  | None =>
@@ -166,7 +148,7 @@ let fromSvmEventConfigsOrThrow = (events: array<Internal.svmInstructionEventConf
166
148
  byProgram->Dict.set(key, s)
167
149
  s
168
150
  }
169
- let _ = set->Utils.Set.add(config.discriminatorByteLen)
151
+ let _ = set->Utils.Set.add(eventConfig.discriminatorByteLen)
170
152
  })
171
153
  let ordering =
172
154
  byProgram
@@ -39,16 +39,13 @@ function addOrThrow(group, event, contractName, isWildcard) {
39
39
  byContractName[contractName] = event;
40
40
  }
41
41
 
42
- function get(group, contractAddress, blockNumber, indexingAddresses) {
42
+ function get(group, contractAddress, contractNameByAddress) {
43
43
  let wildcard = group.wildcard;
44
- let indexingContract = indexingAddresses[contractAddress];
45
- if (indexingContract === undefined) {
44
+ let contractName = contractNameByAddress[contractAddress];
45
+ if (contractName === undefined) {
46
46
  return wildcard;
47
47
  }
48
- if (indexingContract.effectiveStartBlock > blockNumber) {
49
- return;
50
- }
51
- let event = group.byContractName[indexingContract.contractName];
48
+ let event = group.byContractName[contractName];
52
49
  if (event !== undefined) {
53
50
  return event;
54
51
  } else {
@@ -93,23 +90,13 @@ function addOrThrow$1(router, eventId, event, contractName, isWildcard, eventNam
93
90
  }
94
91
  }
95
92
 
96
- function get$1(router, tag, contractAddress, blockNumber, indexingAddresses) {
93
+ function get$1(router, tag, contractAddress, contractNameByAddress) {
97
94
  let group = router[tag];
98
95
  if (group !== undefined) {
99
- return get(group, contractAddress, blockNumber, indexingAddresses);
96
+ return get(group, contractAddress, contractNameByAddress);
100
97
  }
101
98
  }
102
99
 
103
- function getEvmEventId(sighash, topicCount) {
104
- return sighash + "_" + topicCount.toString();
105
- }
106
-
107
- function fromEvmEventModsOrThrow(events, chain) {
108
- let router = {};
109
- events.forEach(config => addOrThrow$1(router, config.id, config, config.contractName, config.isWildcard, config.name, chain));
110
- return router;
111
- }
112
-
113
100
  function getSvmEventId(programId, discriminator) {
114
101
  if (discriminator !== undefined) {
115
102
  return programId + "_" + discriminator;
@@ -121,12 +108,14 @@ function getSvmEventId(programId, discriminator) {
121
108
  function fromSvmEventConfigsOrThrow(events, chain) {
122
109
  let router = {};
123
110
  events.forEach(config => {
124
- let routerTag = getSvmEventId(config.programId, config.discriminator);
125
- addOrThrow$1(router, routerTag, config, config.contractName, config.isWildcard, config.name, chain);
111
+ let eventConfig = config.eventConfig;
112
+ let routerTag = getSvmEventId(eventConfig.programId, eventConfig.discriminator);
113
+ addOrThrow$1(router, routerTag, config, eventConfig.contractName, config.isWildcard, eventConfig.name, chain);
126
114
  });
127
115
  let byProgram = {};
128
116
  events.forEach(config => {
129
- let key = config.programId;
117
+ let eventConfig = config.eventConfig;
118
+ let key = eventConfig.programId;
130
119
  let s = byProgram[key];
131
120
  let set;
132
121
  if (s !== undefined) {
@@ -136,7 +125,7 @@ function fromSvmEventConfigsOrThrow(events, chain) {
136
125
  byProgram[key] = s$1;
137
126
  set = s$1;
138
127
  }
139
- set.add(config.discriminatorByteLen);
128
+ set.add(eventConfig.discriminatorByteLen);
140
129
  });
141
130
  let ordering = Object.entries(byProgram).map(param => {
142
131
  let sorted = Array.from(param[1]).toSorted((a, b) => b - a | 0);
@@ -158,8 +147,6 @@ export {
158
147
  empty$1 as empty,
159
148
  addOrThrow$1 as addOrThrow,
160
149
  get$1 as get,
161
- getEvmEventId,
162
- fromEvmEventModsOrThrow,
163
150
  getSvmEventId,
164
151
  fromSvmEventConfigsOrThrow,
165
152
  }
@@ -1,5 +1,7 @@
1
1
  // EVM's concrete item payload. Erased to `Internal.eventPayload` on the item
2
- // and recovered here via `toPayload`.
2
+ // and recovered here via `toPayload`. HyperSync omits `transaction` (it lives
3
+ // raw in the per-chain store and is written onto the payload at batch prep);
4
+ // RPC/simulate build it inline.
3
5
  type payload = {
4
6
  contractName: string,
5
7
  eventName: string,
@@ -7,12 +9,62 @@ type payload = {
7
9
  chainId: int,
8
10
  srcAddress: Address.t,
9
11
  logIndex: int,
10
- transaction: Internal.eventTransaction,
11
- block: Internal.eventBlock,
12
+ transaction?: Internal.eventTransaction,
13
+ // HyperSync omits `block` (it lives raw in the per-chain store and is written
14
+ // onto the payload at batch prep); RPC/simulate build it inline.
15
+ block?: Internal.eventBlock,
12
16
  }
13
17
  external fromPayload: payload => Internal.eventPayload = "%identity"
14
18
  external toPayload: Internal.eventPayload => payload = "%identity"
15
19
 
20
+ // Ordered transaction field names, the field codes shared with the Rust store
21
+ // (`EvmTxField`). Derived from the typed field list so the two can't drift;
22
+ // `Internal.allEvmTransactionFields` is pinned to the Rust ordinal order by a test.
23
+ let transactionFields =
24
+ Internal.allEvmTransactionFields->(
25
+ Utils.magic: array<Internal.evmTransactionField> => array<string>
26
+ )
27
+
28
+ // One event's selected transaction fields → store selection bitmask. Computed
29
+ // per event at config build and cached on the event config.
30
+ let eventTransactionFieldMask = TransactionStore.makeMaskFn(transactionFields)
31
+
32
+ // Ordered block field names. The index of each is the field code shared with the
33
+ // Rust store (`EvmBlockField`) — keep this order in sync.
34
+ let blockFields = [
35
+ "number",
36
+ "timestamp",
37
+ "hash",
38
+ "parentHash",
39
+ "nonce",
40
+ "sha3Uncles",
41
+ "logsBloom",
42
+ "transactionsRoot",
43
+ "stateRoot",
44
+ "receiptsRoot",
45
+ "miner",
46
+ "difficulty",
47
+ "totalDifficulty",
48
+ "extraData",
49
+ "size",
50
+ "gasLimit",
51
+ "gasUsed",
52
+ "uncles",
53
+ "baseFeePerGas",
54
+ "blobGasUsed",
55
+ "excessBlobGas",
56
+ "parentBeaconBlockRoot",
57
+ "withdrawalsRoot",
58
+ "l1BlockNumber",
59
+ "sendCount",
60
+ "sendRoot",
61
+ "mixHash",
62
+ ]
63
+
64
+ // One event's selected block fields → store selection bitmask. Computed per
65
+ // event at config build and cached on the event config.
66
+ let eventBlockFieldMask = BlockStore.makeMaskFn(blockFields)
67
+
16
68
  let cleanUpRawEventFieldsInPlace: JSON.t => unit = %raw(`fields => {
17
69
  delete fields.hash
18
70
  delete fields.number
@@ -21,70 +73,6 @@ let cleanUpRawEventFieldsInPlace: JSON.t => unit = %raw(`fields => {
21
73
 
22
74
  let make = (~logger: Pino.t): Ecosystem.t => {
23
75
  name: Evm,
24
- blockFields: [
25
- "number",
26
- "timestamp",
27
- "hash",
28
- "parentHash",
29
- "nonce",
30
- "sha3Uncles",
31
- "logsBloom",
32
- "transactionsRoot",
33
- "stateRoot",
34
- "receiptsRoot",
35
- "miner",
36
- "difficulty",
37
- "totalDifficulty",
38
- "extraData",
39
- "size",
40
- "gasLimit",
41
- "gasUsed",
42
- "uncles",
43
- "baseFeePerGas",
44
- "blobGasUsed",
45
- "excessBlobGas",
46
- "parentBeaconBlockRoot",
47
- "withdrawalsRoot",
48
- "l1BlockNumber",
49
- "sendCount",
50
- "sendRoot",
51
- "mixHash",
52
- ],
53
- transactionFields: [
54
- "transactionIndex",
55
- "hash",
56
- "from",
57
- "to",
58
- "gas",
59
- "gasPrice",
60
- "maxPriorityFeePerGas",
61
- "maxFeePerGas",
62
- "cumulativeGasUsed",
63
- "effectiveGasPrice",
64
- "gasUsed",
65
- "input",
66
- "nonce",
67
- "value",
68
- "v",
69
- "r",
70
- "s",
71
- "contractAddress",
72
- "logsBloom",
73
- "root",
74
- "status",
75
- "yParity",
76
- "chainId",
77
- "maxFeePerBlobGas",
78
- "blobVersionedHashes",
79
- "type",
80
- "l1Fee",
81
- "l1GasPrice",
82
- "l1GasUsed",
83
- "l1FeeScalar",
84
- "gasUsedForL1",
85
- "accessList",
86
- "authorizationList",
87
- ],
88
76
  blockNumberName: "number",
89
77
  blockTimestampName: "timestamp",
90
78
  blockHashName: "hash",
@@ -102,17 +90,22 @@ let make = (~logger: Pino.t): Ecosystem.t => {
102
90
  // range chunk is validated by `eventBlockRangeSchema` in
103
91
  // `LogSelection.res` which rejects `_lte`/`_every` (use `onBlock` for
104
92
  // stride- and endBlock-based block handlers).
93
+ // `S.strict` on the inner object rejects unknown `block` fields (e.g. a
94
+ // `numbre` typo or `block: {timestamp: ...}`) instead of silently
95
+ // ignoring them.
105
96
  onEventBlockFilterSchema: S.object(s =>
106
- s.field("block", S.option(S.object(s2 => s2.field("number", S.unknown))))
97
+ s.field("block", S.option(S.object(s2 => s2.field("number", S.unknown))->S.strict))
107
98
  ),
108
99
  logger,
109
- toEvent: eventItem => eventItem.payload->Internal.payloadToEvent,
100
+ // The payload carries `transaction` by batch prep (HyperSync) or inline
101
+ // (RPC/simulate), so the event is the payload as-is.
102
+ toEvent: eventItem => eventItem.payload->(Utils.magic: Internal.eventPayload => Internal.event),
110
103
  toEventLogger: eventItem =>
111
104
  Logging.createChildFrom(
112
105
  ~logger,
113
106
  ~params={
114
- "contract": eventItem.eventConfig.contractName,
115
- "event": eventItem.eventConfig.name,
107
+ "contract": eventItem.onEventRegistration.eventConfig.contractName,
108
+ "event": eventItem.onEventRegistration.eventConfig.name,
116
109
  "chainId": eventItem.chain->ChainMap.Chain.toChainId,
117
110
  "block": eventItem.blockNumber,
118
111
  "logIndex": eventItem.logIndex,
@@ -121,11 +114,29 @@ let make = (~logger: Pino.t): Ecosystem.t => {
121
114
  ),
122
115
  toRawEvent: eventItem => {
123
116
  let payload = eventItem.payload->toPayload
117
+ let eventConfig =
118
+ eventItem.onEventRegistration.eventConfig->(
119
+ Utils.magic: Internal.eventConfig => Internal.evmEventConfig
120
+ )
121
+ // Store-backed payloads get `block` written at batch prep and inline
122
+ // sources carry it from the start, with hash/timestamp always selected —
123
+ // so both are present by the time a raw event is built.
124
+ let header = switch payload.block {
125
+ | Some(block) => block->(Utils.magic: Internal.eventBlock => {"hash": string, "timestamp": int})
126
+ | None =>
127
+ JsError.throwWithMessage("Unexpected case: The event block is missing for a raw event")
128
+ }
124
129
  eventItem->RawEvent.make(
125
130
  ~block=payload.block,
126
131
  ~transaction=payload.transaction,
127
- ~params=payload.params,
132
+ // The decoder emits `{}` for zero-parameter events, which the params
133
+ // schema rejects; pass unit so it serializes to the "null" sentinel.
134
+ ~params=eventConfig.paramsMetadata->Array.length == 0
135
+ ? ()->(Utils.magic: unit => Internal.eventParams)
136
+ : payload.params,
128
137
  ~srcAddress=payload.srcAddress,
138
+ ~blockHash=header["hash"],
139
+ ~blockTimestamp=header["timestamp"],
129
140
  ~cleanUpRawEventFieldsInPlace,
130
141
  )
131
142
  },