envio 3.3.0-alpha.7 → 3.3.0-alpha.9

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 (95) hide show
  1. package/index.d.ts +23 -5
  2. package/package.json +6 -6
  3. package/src/Api.res +1 -1
  4. package/src/Api.res.mjs +1 -1
  5. package/src/ChainFetching.res +21 -10
  6. package/src/ChainFetching.res.mjs +11 -10
  7. package/src/ChainState.res +401 -171
  8. package/src/ChainState.res.mjs +270 -104
  9. package/src/ChainState.resi +19 -7
  10. package/src/Config.res +11 -27
  11. package/src/Config.res.mjs +8 -7
  12. package/src/Core.res +7 -0
  13. package/src/CrossChainState.res +82 -82
  14. package/src/CrossChainState.res.mjs +52 -59
  15. package/src/CrossChainState.resi +1 -1
  16. package/src/Ecosystem.res +3 -3
  17. package/src/Ecosystem.res.mjs +3 -3
  18. package/src/Envio.res +14 -9
  19. package/src/EnvioGlobal.res +53 -0
  20. package/src/EnvioGlobal.res.mjs +31 -0
  21. package/src/EventConfigBuilder.res +117 -69
  22. package/src/EventConfigBuilder.res.mjs +71 -34
  23. package/src/EventProcessing.res +19 -15
  24. package/src/EventProcessing.res.mjs +13 -12
  25. package/src/FetchState.res +410 -185
  26. package/src/FetchState.res.mjs +268 -253
  27. package/src/HandlerLoader.res +8 -113
  28. package/src/HandlerLoader.res.mjs +2 -88
  29. package/src/HandlerRegister.res +518 -144
  30. package/src/HandlerRegister.res.mjs +285 -133
  31. package/src/HandlerRegister.resi +24 -8
  32. package/src/IndexerState.res +6 -3
  33. package/src/IndexerState.res.mjs +3 -3
  34. package/src/IndexerState.resi +1 -1
  35. package/src/IndexingAddresses.res +10 -7
  36. package/src/IndexingAddresses.res.mjs +8 -7
  37. package/src/IndexingAddresses.resi +3 -1
  38. package/src/Internal.res +104 -39
  39. package/src/Internal.res.mjs +11 -1
  40. package/src/LogSelection.res +102 -165
  41. package/src/LogSelection.res.mjs +101 -116
  42. package/src/Main.res +49 -164
  43. package/src/Main.res.mjs +39 -104
  44. package/src/Metrics.res +43 -2
  45. package/src/Metrics.res.mjs +39 -3
  46. package/src/Prometheus.res +0 -35
  47. package/src/Prometheus.res.mjs +33 -68
  48. package/src/RawEvent.res +7 -2
  49. package/src/RawEvent.res.mjs +4 -4
  50. package/src/RollbackCommit.res +4 -1
  51. package/src/RollbackCommit.res.mjs +3 -2
  52. package/src/SimulateItems.res +20 -7
  53. package/src/SimulateItems.res.mjs +7 -11
  54. package/src/TestIndexer.res +1 -1
  55. package/src/TestIndexer.res.mjs +1 -1
  56. package/src/sources/BlockStore.res +46 -0
  57. package/src/sources/BlockStore.res.mjs +24 -0
  58. package/src/sources/EventRouter.res +19 -12
  59. package/src/sources/EventRouter.res.mjs +7 -5
  60. package/src/sources/Evm.res +58 -5
  61. package/src/sources/Evm.res.mjs +44 -5
  62. package/src/sources/EvmChain.res +14 -18
  63. package/src/sources/EvmChain.res.mjs +12 -13
  64. package/src/sources/FieldMask.res +39 -0
  65. package/src/sources/FieldMask.res.mjs +42 -0
  66. package/src/sources/Fuel.res +8 -3
  67. package/src/sources/Fuel.res.mjs +5 -4
  68. package/src/sources/HyperFuelSource.res +31 -40
  69. package/src/sources/HyperFuelSource.res.mjs +52 -44
  70. package/src/sources/HyperSync.res +31 -9
  71. package/src/sources/HyperSync.res.mjs +47 -31
  72. package/src/sources/HyperSync.resi +10 -3
  73. package/src/sources/HyperSyncClient.res +15 -4
  74. package/src/sources/HyperSyncHeightStream.res +1 -8
  75. package/src/sources/HyperSyncHeightStream.res.mjs +0 -2
  76. package/src/sources/HyperSyncSource.res +58 -90
  77. package/src/sources/HyperSyncSource.res.mjs +57 -66
  78. package/src/sources/RpcSource.res +118 -120
  79. package/src/sources/RpcSource.res.mjs +92 -74
  80. package/src/sources/RpcWebSocketHeightStream.res +0 -5
  81. package/src/sources/RpcWebSocketHeightStream.res.mjs +0 -2
  82. package/src/sources/SimulateSource.res +5 -3
  83. package/src/sources/SimulateSource.res.mjs +12 -4
  84. package/src/sources/Source.res +19 -5
  85. package/src/sources/SourceManager.res +60 -10
  86. package/src/sources/SourceManager.res.mjs +54 -8
  87. package/src/sources/SourceManager.resi +10 -0
  88. package/src/sources/Svm.res +14 -10
  89. package/src/sources/Svm.res.mjs +23 -6
  90. package/src/sources/SvmHyperSyncClient.res +5 -6
  91. package/src/sources/SvmHyperSyncSource.res +82 -41
  92. package/src/sources/SvmHyperSyncSource.res.mjs +86 -41
  93. package/src/sources/TransactionStore.res +6 -107
  94. package/src/sources/TransactionStore.res.mjs +5 -86
  95. package/svm.schema.json +19 -0
@@ -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 */
@@ -96,16 +96,16 @@ let getEvmEventId = (~sighash, ~topicCount) => {
96
96
  sighash ++ "_" ++ topicCount->Int.toString
97
97
  }
98
98
 
99
- let fromEvmEventModsOrThrow = (events: array<Internal.evmEventConfig>, ~chain): t<
100
- Internal.evmEventConfig,
99
+ let fromEvmEventModsOrThrow = (events: array<Internal.evmOnEventRegistration>, ~chain): t<
100
+ Internal.evmOnEventRegistration,
101
101
  > => {
102
102
  let router = empty()
103
103
  events->Array.forEach(config => {
104
104
  router->addOrThrow(
105
- config.id,
105
+ config.eventConfig.id,
106
106
  config,
107
- ~contractName=config.contractName,
108
- ~eventName=config.name,
107
+ ~contractName=config.eventConfig.contractName,
108
+ ~eventName=config.eventConfig.name,
109
109
  ~chain,
110
110
  ~isWildcard=config.isWildcard,
111
111
  )
@@ -131,21 +131,26 @@ type svmProgramOrdering = {
131
131
  byteLengthsDesc: array<int>,
132
132
  }
133
133
 
134
- let fromSvmEventConfigsOrThrow = (events: array<Internal.svmInstructionEventConfig>, ~chain): (
135
- t<Internal.svmInstructionEventConfig>,
134
+ let fromSvmEventConfigsOrThrow = (events: array<Internal.svmOnEventRegistration>, ~chain): (
135
+ t<Internal.svmOnEventRegistration>,
136
136
  array<svmProgramOrdering>,
137
137
  ) => {
138
138
  let router = empty()
139
139
  events->Array.forEach(config => {
140
+ let eventConfig =
141
+ config.eventConfig->(Utils.magic: Internal.eventConfig => Internal.svmInstructionEventConfig)
140
142
  // The router tag must include the programId so two programs declaring the
141
143
  // same discriminator coexist. The source-side lookup uses the same shape
142
144
  // via `getSvmEventId(~programId, ~discriminator)`.
143
- let routerTag = getSvmEventId(~programId=config.programId, ~discriminator=config.discriminator)
145
+ let routerTag = getSvmEventId(
146
+ ~programId=eventConfig.programId,
147
+ ~discriminator=eventConfig.discriminator,
148
+ )
144
149
  router->addOrThrow(
145
150
  routerTag,
146
151
  config,
147
- ~contractName=config.contractName,
148
- ~eventName=config.name,
152
+ ~contractName=eventConfig.contractName,
153
+ ~eventName=eventConfig.name,
149
154
  ~chain,
150
155
  ~isWildcard=config.isWildcard,
151
156
  )
@@ -154,7 +159,9 @@ let fromSvmEventConfigsOrThrow = (events: array<Internal.svmInstructionEventConf
154
159
  // Per-program list of declared discriminator byte lengths, sorted desc.
155
160
  let byProgram: dict<Utils.Set.t<int>> = Dict.make()
156
161
  events->Array.forEach(config => {
157
- let key = config.programId->SvmTypes.Pubkey.toString
162
+ let eventConfig =
163
+ config.eventConfig->(Utils.magic: Internal.eventConfig => Internal.svmInstructionEventConfig)
164
+ let key = eventConfig.programId->SvmTypes.Pubkey.toString
158
165
  let set = switch byProgram->Utils.Dict.dangerouslyGetNonOption(key) {
159
166
  | Some(s) => s
160
167
  | None =>
@@ -162,7 +169,7 @@ let fromSvmEventConfigsOrThrow = (events: array<Internal.svmInstructionEventConf
162
169
  byProgram->Dict.set(key, s)
163
170
  s
164
171
  }
165
- let _ = set->Utils.Set.add(config.discriminatorByteLen)
172
+ let _ = set->Utils.Set.add(eventConfig.discriminatorByteLen)
166
173
  })
167
174
  let ordering =
168
175
  byProgram
@@ -103,7 +103,7 @@ function getEvmEventId(sighash, topicCount) {
103
103
 
104
104
  function fromEvmEventModsOrThrow(events, chain) {
105
105
  let router = {};
106
- events.forEach(config => addOrThrow$1(router, config.id, config, config.contractName, config.isWildcard, config.name, chain));
106
+ events.forEach(config => addOrThrow$1(router, config.eventConfig.id, config, config.eventConfig.contractName, config.isWildcard, config.eventConfig.name, chain));
107
107
  return router;
108
108
  }
109
109
 
@@ -118,12 +118,14 @@ function getSvmEventId(programId, discriminator) {
118
118
  function fromSvmEventConfigsOrThrow(events, chain) {
119
119
  let router = {};
120
120
  events.forEach(config => {
121
- let routerTag = getSvmEventId(config.programId, config.discriminator);
122
- addOrThrow$1(router, routerTag, config, config.contractName, config.isWildcard, config.name, chain);
121
+ let eventConfig = config.eventConfig;
122
+ let routerTag = getSvmEventId(eventConfig.programId, eventConfig.discriminator);
123
+ addOrThrow$1(router, routerTag, config, eventConfig.contractName, config.isWildcard, eventConfig.name, chain);
123
124
  });
124
125
  let byProgram = {};
125
126
  events.forEach(config => {
126
- let key = config.programId;
127
+ let eventConfig = config.eventConfig;
128
+ let key = eventConfig.programId;
127
129
  let s = byProgram[key];
128
130
  let set;
129
131
  if (s !== undefined) {
@@ -133,7 +135,7 @@ function fromSvmEventConfigsOrThrow(events, chain) {
133
135
  byProgram[key] = s$1;
134
136
  set = s$1;
135
137
  }
136
- set.add(config.discriminatorByteLen);
138
+ set.add(eventConfig.discriminatorByteLen);
137
139
  });
138
140
  let ordering = Object.entries(byProgram).map(param => {
139
141
  let sorted = Array.from(param[1]).toSorted((a, b) => b - a | 0);
@@ -10,7 +10,9 @@ type payload = {
10
10
  srcAddress: Address.t,
11
11
  logIndex: int,
12
12
  transaction?: Internal.eventTransaction,
13
- block: Internal.eventBlock,
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,
14
16
  }
15
17
  external fromPayload: payload => Internal.eventPayload = "%identity"
16
18
  external toPayload: Internal.eventPayload => payload = "%identity"
@@ -27,6 +29,42 @@ let transactionFields =
27
29
  // per event at config build and cached on the event config.
28
30
  let eventTransactionFieldMask = TransactionStore.makeMaskFn(transactionFields)
29
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
+
30
68
  let cleanUpRawEventFieldsInPlace: JSON.t => unit = %raw(`fields => {
31
69
  delete fields.hash
32
70
  delete fields.number
@@ -52,8 +90,11 @@ let make = (~logger: Pino.t): Ecosystem.t => {
52
90
  // range chunk is validated by `eventBlockRangeSchema` in
53
91
  // `LogSelection.res` which rejects `_lte`/`_every` (use `onBlock` for
54
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.
55
96
  onEventBlockFilterSchema: S.object(s =>
56
- 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))
57
98
  ),
58
99
  logger,
59
100
  // The payload carries `transaction` by batch prep (HyperSync) or inline
@@ -63,8 +104,8 @@ let make = (~logger: Pino.t): Ecosystem.t => {
63
104
  Logging.createChildFrom(
64
105
  ~logger,
65
106
  ~params={
66
- "contract": eventItem.eventConfig.contractName,
67
- "event": eventItem.eventConfig.name,
107
+ "contract": eventItem.onEventRegistration.eventConfig.contractName,
108
+ "event": eventItem.onEventRegistration.eventConfig.name,
68
109
  "chainId": eventItem.chain->ChainMap.Chain.toChainId,
69
110
  "block": eventItem.blockNumber,
70
111
  "logIndex": eventItem.logIndex,
@@ -74,7 +115,17 @@ let make = (~logger: Pino.t): Ecosystem.t => {
74
115
  toRawEvent: eventItem => {
75
116
  let payload = eventItem.payload->toPayload
76
117
  let eventConfig =
77
- eventItem.eventConfig->(Utils.magic: Internal.eventConfig => Internal.evmEventConfig)
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
+ }
78
129
  eventItem->RawEvent.make(
79
130
  ~block=payload.block,
80
131
  ~transaction=payload.transaction,
@@ -84,6 +135,8 @@ let make = (~logger: Pino.t): Ecosystem.t => {
84
135
  ? ()->(Utils.magic: unit => Internal.eventParams)
85
136
  : payload.params,
86
137
  ~srcAddress=payload.srcAddress,
138
+ ~blockHash=header["hash"],
139
+ ~blockTimestamp=header["timestamp"],
87
140
  ~cleanUpRawEventFieldsInPlace,
88
141
  )
89
142
  },
@@ -3,11 +3,46 @@
3
3
  import * as Logging from "../Logging.res.mjs";
4
4
  import * as Internal from "../Internal.res.mjs";
5
5
  import * as RawEvent from "../RawEvent.res.mjs";
6
+ import * as BlockStore from "./BlockStore.res.mjs";
7
+ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
8
+ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
6
9
  import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
7
10
  import * as TransactionStore from "./TransactionStore.res.mjs";
8
11
 
9
12
  let eventTransactionFieldMask = TransactionStore.makeMaskFn(Internal.allEvmTransactionFields);
10
13
 
14
+ let blockFields = [
15
+ "number",
16
+ "timestamp",
17
+ "hash",
18
+ "parentHash",
19
+ "nonce",
20
+ "sha3Uncles",
21
+ "logsBloom",
22
+ "transactionsRoot",
23
+ "stateRoot",
24
+ "receiptsRoot",
25
+ "miner",
26
+ "difficulty",
27
+ "totalDifficulty",
28
+ "extraData",
29
+ "size",
30
+ "gasLimit",
31
+ "gasUsed",
32
+ "uncles",
33
+ "baseFeePerGas",
34
+ "blobGasUsed",
35
+ "excessBlobGas",
36
+ "parentBeaconBlockRoot",
37
+ "withdrawalsRoot",
38
+ "l1BlockNumber",
39
+ "sendCount",
40
+ "sendRoot",
41
+ "mixHash"
42
+ ];
43
+
44
+ let eventBlockFieldMask = BlockStore.makeMaskFn(blockFields);
45
+
11
46
  let cleanUpRawEventFieldsInPlace = (fields => {
12
47
  delete fields.hash
13
48
  delete fields.number
@@ -23,12 +58,12 @@ function make(logger) {
23
58
  cleanUpRawEventFieldsInPlace: cleanUpRawEventFieldsInPlace,
24
59
  onBlockMethodName: "onBlock",
25
60
  onBlockFilterSchema: S$RescriptSchema.object(s => s.f("block", S$RescriptSchema.option(S$RescriptSchema.object(s2 => s2.f("number", S$RescriptSchema.unknown))))),
26
- onEventBlockFilterSchema: S$RescriptSchema.object(s => s.f("block", S$RescriptSchema.option(S$RescriptSchema.object(s2 => s2.f("number", S$RescriptSchema.unknown))))),
61
+ onEventBlockFilterSchema: S$RescriptSchema.object(s => s.f("block", S$RescriptSchema.option(S$RescriptSchema.strict(S$RescriptSchema.object(s2 => s2.f("number", S$RescriptSchema.unknown)))))),
27
62
  logger: logger,
28
63
  toEvent: eventItem => eventItem.payload,
29
64
  toEventLogger: eventItem => Logging.createChildFrom(logger, {
30
- contract: eventItem.eventConfig.contractName,
31
- event: eventItem.eventConfig.name,
65
+ contract: eventItem.onEventRegistration.eventConfig.contractName,
66
+ event: eventItem.onEventRegistration.eventConfig.name,
32
67
  chainId: eventItem.chain,
33
68
  block: eventItem.blockNumber,
34
69
  logIndex: eventItem.logIndex,
@@ -36,8 +71,10 @@ function make(logger) {
36
71
  }),
37
72
  toRawEvent: eventItem => {
38
73
  let payload = eventItem.payload;
39
- let eventConfig = eventItem.eventConfig;
40
- return RawEvent.make(eventItem, payload.block, payload.transaction, eventConfig.paramsMetadata.length === 0 ? undefined : payload.params, payload.srcAddress, cleanUpRawEventFieldsInPlace);
74
+ let eventConfig = eventItem.onEventRegistration.eventConfig;
75
+ let block = payload.block;
76
+ let header = block !== undefined ? Primitive_option.valFromOption(block) : Stdlib_JsError.throwWithMessage("Unexpected case: The event block is missing for a raw event");
77
+ return RawEvent.make(eventItem, payload.block, payload.transaction, eventConfig.paramsMetadata.length === 0 ? undefined : payload.params, payload.srcAddress, header.hash, header.timestamp, cleanUpRawEventFieldsInPlace);
41
78
  }
42
79
  };
43
80
  }
@@ -47,6 +84,8 @@ let transactionFields = Internal.allEvmTransactionFields;
47
84
  export {
48
85
  transactionFields,
49
86
  eventTransactionFieldMask,
87
+ blockFields,
88
+ eventBlockFieldMask,
50
89
  cleanUpRawEventFieldsInPlace,
51
90
  make,
52
91
  }
@@ -39,39 +39,35 @@ let getSyncConfig = (
39
39
  }
40
40
  }
41
41
 
42
- let collectEventParams = (contracts: array<Internal.evmContractConfig>): array<
42
+ let collectEventParams = (onEventRegistrations: array<Internal.evmOnEventRegistration>): array<
43
43
  HyperSyncClient.Decoder.eventParamsInput,
44
44
  > => {
45
45
  let result = []
46
- contracts->Array.forEach(contract => {
47
- contract.events->Array.forEach(event => {
48
- result
49
- ->Array.push({
50
- HyperSyncClient.Decoder.sighash: event.sighash,
51
- topicCount: event.topicCount,
52
- eventName: event.name,
53
- contractName: contract.name,
54
- params: event.paramsMetadata,
55
- })
56
- ->ignore
46
+ onEventRegistrations->Array.forEach(reg => {
47
+ let event = reg.eventConfig->(Utils.magic: Internal.eventConfig => Internal.evmEventConfig)
48
+ result
49
+ ->Array.push({
50
+ HyperSyncClient.Decoder.sighash: event.sighash,
51
+ topicCount: event.topicCount,
52
+ eventName: event.name,
53
+ contractName: event.contractName,
54
+ params: event.paramsMetadata,
57
55
  })
56
+ ->ignore
58
57
  })
59
58
  result
60
59
  }
61
60
 
62
61
  let makeSources = (
63
62
  ~chain,
64
- ~contracts: array<Internal.evmContractConfig>,
63
+ ~onEventRegistrations: array<Internal.evmOnEventRegistration>,
65
64
  ~hyperSync,
66
65
  ~rpcs: array<rpc>,
67
66
  ~lowercaseAddresses,
68
67
  ) => {
69
- let eventRouter =
70
- contracts
71
- ->Array.flatMap(contract => contract.events)
72
- ->EventRouter.fromEvmEventModsOrThrow(~chain)
68
+ let eventRouter = onEventRegistrations->EventRouter.fromEvmEventModsOrThrow(~chain)
73
69
 
74
- let allEventParams = collectEventParams(contracts)
70
+ let allEventParams = collectEventParams(onEventRegistrations)
75
71
 
76
72
  let sources = switch hyperSync {
77
73
  | Some(endpointUrl) => [
@@ -20,25 +20,24 @@ function getSyncConfig(param) {
20
20
  };
21
21
  }
22
22
 
23
- function collectEventParams(contracts) {
23
+ function collectEventParams(onEventRegistrations) {
24
24
  let result = [];
25
- contracts.forEach(contract => {
26
- contract.events.forEach(event => {
27
- result.push({
28
- sighash: event.sighash,
29
- topicCount: event.topicCount,
30
- eventName: event.name,
31
- contractName: contract.name,
32
- params: event.paramsMetadata
33
- });
25
+ onEventRegistrations.forEach(reg => {
26
+ let event = reg.eventConfig;
27
+ result.push({
28
+ sighash: event.sighash,
29
+ topicCount: event.topicCount,
30
+ eventName: event.name,
31
+ contractName: event.contractName,
32
+ params: event.paramsMetadata
34
33
  });
35
34
  });
36
35
  return result;
37
36
  }
38
37
 
39
- function makeSources(chain, contracts, hyperSync, rpcs, lowercaseAddresses) {
40
- let eventRouter = EventRouter.fromEvmEventModsOrThrow(contracts.flatMap(contract => contract.events), chain);
41
- let allEventParams = collectEventParams(contracts);
38
+ function makeSources(chain, onEventRegistrations, hyperSync, rpcs, lowercaseAddresses) {
39
+ let eventRouter = EventRouter.fromEvmEventModsOrThrow(onEventRegistrations, chain);
40
+ let allEventParams = collectEventParams(onEventRegistrations);
42
41
  let sources = hyperSync !== undefined ? [HyperSyncSource.make({
43
42
  chain: chain,
44
43
  endpointUrl: hyperSync,
@@ -0,0 +1,39 @@
1
+ // Selection-bitmask helpers shared by the per-chain field stores
2
+ // (`TransactionStore`, `BlockStore`). A field's bit index is its position in the
3
+ // ecosystem's ordered field-name array — the same code the Rust store uses.
4
+
5
+ // Field-name → bit-index map from an ordered field-name array.
6
+ let fieldCodes = (fields: array<string>): dict<int> => {
7
+ let codes = Dict.make()
8
+ fields->Array.forEachWithIndex((name, i) => codes->Dict.set(name, i))
9
+ codes
10
+ }
11
+
12
+ let pow2: int => float = %raw(`c => Math.pow(2, c)`)
13
+
14
+ // One event's selected fields as a bitmask float (bit `code` set ⇔ selected).
15
+ // Each field appears once, so summing `pow2(code)` sets distinct bits with no
16
+ // overlap; the result stays exact in f64 (codes span 0..31, so a mask is at most
17
+ // 2^32-1).
18
+ let maskFromFields = (selectedFields: Utils.Set.t<string>, ~codes: dict<int>): float => {
19
+ let mask = ref(0.)
20
+ selectedFields->Utils.Set.forEach(name =>
21
+ switch codes->Utils.Dict.dangerouslyGetNonOption(name) {
22
+ | Some(code) => mask := mask.contents +. pow2(code)
23
+ | None => ()
24
+ }
25
+ )
26
+ mask.contents
27
+ }
28
+
29
+ // Build an ecosystem's per-event mask function from its ordered field-name
30
+ // array. The field codes are derived once and closed over.
31
+ let makeMaskFn = (fields: array<string>): (Utils.Set.t<string> => float) => {
32
+ let codes = fieldCodes(fields)
33
+ selectedFields => selectedFields->maskFromFields(~codes)
34
+ }
35
+
36
+ // Bitwise OR of two per-event masks. Masks fit in 32 bits (≤32 fields), so
37
+ // `>>> 0` recovers the unsigned value that a plain `|` renders negative once bit
38
+ // 31 is set.
39
+ let orMask: (float, float) => float = %raw(`(a, b) => (a | b) >>> 0`)
@@ -0,0 +1,42 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+
4
+ function fieldCodes(fields) {
5
+ let codes = {};
6
+ fields.forEach((name, i) => {
7
+ codes[name] = i;
8
+ });
9
+ return codes;
10
+ }
11
+
12
+ let pow2 = (c => Math.pow(2, c));
13
+
14
+ function maskFromFields(selectedFields, codes) {
15
+ let mask = {
16
+ contents: 0
17
+ };
18
+ selectedFields.forEach(name => {
19
+ let code = codes[name];
20
+ if (code !== undefined) {
21
+ mask.contents = mask.contents + pow2(code);
22
+ return;
23
+ }
24
+ });
25
+ return mask.contents;
26
+ }
27
+
28
+ function makeMaskFn(fields) {
29
+ let codes = fieldCodes(fields);
30
+ return selectedFields => maskFromFields(selectedFields, codes);
31
+ }
32
+
33
+ let orMask = ((a, b) => (a | b) >>> 0);
34
+
35
+ export {
36
+ fieldCodes,
37
+ pow2,
38
+ maskFromFields,
39
+ makeMaskFn,
40
+ orMask,
41
+ }
42
+ /* No side effect */
@@ -35,8 +35,10 @@ let make = (~logger: Pino.t): Ecosystem.t => {
35
35
  // Analogous to EVM, but keyed by `block.height` instead of
36
36
  // `block.number`. See `Evm.res` for the rationale on the two-stage
37
37
  // parse and the `_lte`/`_every` rejection.
38
+ // `S.strict` on the inner object rejects unknown `block` fields — including
39
+ // `block.number`, which is EVM-only; Fuel filters by `block.height`.
38
40
  onEventBlockFilterSchema: S.object(s =>
39
- s.field("block", S.option(S.object(s2 => s2.field("height", S.unknown))))
41
+ s.field("block", S.option(S.object(s2 => s2.field("height", S.unknown))->S.strict))
40
42
  ),
41
43
  logger,
42
44
  toEvent: eventItem => eventItem.payload->(Utils.magic: Internal.eventPayload => Internal.event),
@@ -44,8 +46,8 @@ let make = (~logger: Pino.t): Ecosystem.t => {
44
46
  Logging.createChildFrom(
45
47
  ~logger,
46
48
  ~params={
47
- "contract": eventItem.eventConfig.contractName,
48
- "event": eventItem.eventConfig.name,
49
+ "contract": eventItem.onEventRegistration.eventConfig.contractName,
50
+ "event": eventItem.onEventRegistration.eventConfig.name,
49
51
  "chainId": eventItem.chain->ChainMap.Chain.toChainId,
50
52
  "block": eventItem.blockNumber,
51
53
  "logIndex": eventItem.logIndex,
@@ -54,11 +56,14 @@ let make = (~logger: Pino.t): Ecosystem.t => {
54
56
  ),
55
57
  toRawEvent: eventItem => {
56
58
  let payload = eventItem.payload->toPayload
59
+ let header = payload.block->(Utils.magic: Internal.eventBlock => {"id": string, "time": int})
57
60
  eventItem->RawEvent.make(
58
61
  ~block=payload.block,
59
62
  ~transaction=payload.transaction,
60
63
  ~params=payload.params,
61
64
  ~srcAddress=payload.srcAddress,
65
+ ~blockHash=header["id"],
66
+ ~blockTimestamp=header["time"],
62
67
  ~cleanUpRawEventFieldsInPlace,
63
68
  )
64
69
  },
@@ -19,12 +19,12 @@ function make(logger) {
19
19
  cleanUpRawEventFieldsInPlace: cleanUpRawEventFieldsInPlace,
20
20
  onBlockMethodName: "onBlock",
21
21
  onBlockFilterSchema: S$RescriptSchema.object(s => s.f("block", S$RescriptSchema.option(S$RescriptSchema.object(s2 => s2.f("height", S$RescriptSchema.unknown))))),
22
- onEventBlockFilterSchema: S$RescriptSchema.object(s => s.f("block", S$RescriptSchema.option(S$RescriptSchema.object(s2 => s2.f("height", S$RescriptSchema.unknown))))),
22
+ onEventBlockFilterSchema: S$RescriptSchema.object(s => s.f("block", S$RescriptSchema.option(S$RescriptSchema.strict(S$RescriptSchema.object(s2 => s2.f("height", S$RescriptSchema.unknown)))))),
23
23
  logger: logger,
24
24
  toEvent: eventItem => eventItem.payload,
25
25
  toEventLogger: eventItem => Logging.createChildFrom(logger, {
26
- contract: eventItem.eventConfig.contractName,
27
- event: eventItem.eventConfig.name,
26
+ contract: eventItem.onEventRegistration.eventConfig.contractName,
27
+ event: eventItem.onEventRegistration.eventConfig.name,
28
28
  chainId: eventItem.chain,
29
29
  block: eventItem.blockNumber,
30
30
  logIndex: eventItem.logIndex,
@@ -32,7 +32,8 @@ function make(logger) {
32
32
  }),
33
33
  toRawEvent: eventItem => {
34
34
  let payload = eventItem.payload;
35
- return RawEvent.make(eventItem, payload.block, payload.transaction, payload.params, payload.srcAddress, cleanUpRawEventFieldsInPlace);
35
+ let header = payload.block;
36
+ return RawEvent.make(eventItem, payload.block, payload.transaction, payload.params, payload.srcAddress, header.id, header.time, cleanUpRawEventFieldsInPlace);
36
37
  }
37
38
  };
38
39
  }