envio 3.3.0-alpha.1 → 3.3.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/evm.schema.json +10 -0
  2. package/index.d.ts +45 -25
  3. package/package.json +7 -7
  4. package/src/Address.res +5 -2
  5. package/src/Address.res.mjs +3 -1
  6. package/src/Api.res +1 -1
  7. package/src/Api.res.mjs +1 -1
  8. package/src/Batch.res +0 -6
  9. package/src/Batch.res.mjs +1 -12
  10. package/src/BatchProcessing.res +7 -12
  11. package/src/BatchProcessing.res.mjs +8 -7
  12. package/src/Bin.res +3 -0
  13. package/src/Bin.res.mjs +4 -0
  14. package/src/ChainFetching.res +43 -9
  15. package/src/ChainFetching.res.mjs +34 -15
  16. package/src/ChainMetadata.res +1 -11
  17. package/src/ChainMetadata.res.mjs +1 -10
  18. package/src/ChainState.res +605 -151
  19. package/src/ChainState.res.mjs +427 -104
  20. package/src/ChainState.resi +72 -4
  21. package/src/Config.res +61 -33
  22. package/src/Config.res.mjs +50 -10
  23. package/src/ContractRegisterContext.res +2 -12
  24. package/src/ContractRegisterContext.res.mjs +3 -5
  25. package/src/Core.res +14 -3
  26. package/src/CrossChainState.res +113 -89
  27. package/src/CrossChainState.res.mjs +64 -60
  28. package/src/CrossChainState.resi +1 -1
  29. package/src/Ecosystem.res +5 -6
  30. package/src/Ecosystem.res.mjs +3 -3
  31. package/src/Envio.res +17 -11
  32. package/src/EnvioGlobal.res +53 -0
  33. package/src/EnvioGlobal.res.mjs +31 -0
  34. package/src/EventConfigBuilder.res +177 -88
  35. package/src/EventConfigBuilder.res.mjs +99 -42
  36. package/src/EventProcessing.res +74 -28
  37. package/src/EventProcessing.res.mjs +55 -29
  38. package/src/ExitOnCaughtUp.res +10 -2
  39. package/src/ExitOnCaughtUp.res.mjs +10 -1
  40. package/src/FetchState.res +544 -356
  41. package/src/FetchState.res.mjs +412 -471
  42. package/src/HandlerLoader.res +8 -104
  43. package/src/HandlerLoader.res.mjs +2 -81
  44. package/src/HandlerRegister.res +518 -144
  45. package/src/HandlerRegister.res.mjs +285 -133
  46. package/src/HandlerRegister.resi +24 -8
  47. package/src/IndexerState.res +10 -3
  48. package/src/IndexerState.res.mjs +11 -4
  49. package/src/IndexerState.resi +2 -1
  50. package/src/IndexingAddresses.res +108 -0
  51. package/src/IndexingAddresses.res.mjs +101 -0
  52. package/src/IndexingAddresses.resi +34 -0
  53. package/src/Internal.res +155 -46
  54. package/src/Internal.res.mjs +30 -2
  55. package/src/LoadLayer.res +5 -5
  56. package/src/LoadLayer.res.mjs +6 -6
  57. package/src/LogSelection.res +102 -165
  58. package/src/LogSelection.res.mjs +101 -116
  59. package/src/Main.res +71 -236
  60. package/src/Main.res.mjs +60 -145
  61. package/src/Metrics.res +74 -0
  62. package/src/Metrics.res.mjs +75 -0
  63. package/src/PgStorage.res +4 -4
  64. package/src/PgStorage.res.mjs +5 -5
  65. package/src/Prometheus.res +10 -63
  66. package/src/Prometheus.res.mjs +124 -171
  67. package/src/PruneStaleHistory.res +2 -2
  68. package/src/PruneStaleHistory.res.mjs +3 -3
  69. package/src/RawEvent.res +7 -2
  70. package/src/RawEvent.res.mjs +4 -4
  71. package/src/Rollback.res +3 -3
  72. package/src/Rollback.res.mjs +4 -4
  73. package/src/RollbackCommit.res +4 -1
  74. package/src/RollbackCommit.res.mjs +3 -2
  75. package/src/SimulateDeadInputTracker.res +85 -0
  76. package/src/SimulateDeadInputTracker.res.mjs +73 -0
  77. package/src/SimulateDeadInputTracker.resi +12 -0
  78. package/src/SimulateItems.res +91 -23
  79. package/src/SimulateItems.res.mjs +55 -32
  80. package/src/TestIndexer.res +54 -40
  81. package/src/TestIndexer.res.mjs +37 -30
  82. package/src/bindings/Performance.res +7 -0
  83. package/src/bindings/Performance.res.mjs +21 -0
  84. package/src/bindings/Performance.resi +7 -0
  85. package/src/bindings/Viem.res +0 -41
  86. package/src/bindings/Viem.res.mjs +1 -43
  87. package/src/sources/BlockStore.res +46 -0
  88. package/src/sources/BlockStore.res.mjs +24 -0
  89. package/src/sources/EventRouter.res +36 -33
  90. package/src/sources/EventRouter.res.mjs +13 -14
  91. package/src/sources/Evm.res +83 -72
  92. package/src/sources/Evm.res.mjs +53 -69
  93. package/src/sources/EvmChain.res +18 -20
  94. package/src/sources/EvmChain.res.mjs +15 -15
  95. package/src/sources/EvmRpcClient.res +74 -5
  96. package/src/sources/EvmRpcClient.res.mjs +13 -4
  97. package/src/sources/FieldMask.res +39 -0
  98. package/src/sources/FieldMask.res.mjs +42 -0
  99. package/src/sources/Fuel.res +9 -6
  100. package/src/sources/Fuel.res.mjs +5 -10
  101. package/src/sources/HyperFuelSource.res +46 -50
  102. package/src/sources/HyperFuelSource.res.mjs +64 -54
  103. package/src/sources/HyperSync.res +39 -7
  104. package/src/sources/HyperSync.res.mjs +57 -37
  105. package/src/sources/HyperSync.resi +12 -2
  106. package/src/sources/HyperSyncClient.res +22 -111
  107. package/src/sources/HyperSyncClient.res.mjs +2 -30
  108. package/src/sources/HyperSyncHeightStream.res +1 -8
  109. package/src/sources/HyperSyncHeightStream.res.mjs +0 -2
  110. package/src/sources/HyperSyncSource.res +94 -107
  111. package/src/sources/HyperSyncSource.res.mjs +82 -86
  112. package/src/sources/Rpc.res +15 -47
  113. package/src/sources/Rpc.res.mjs +25 -56
  114. package/src/sources/RpcSource.res +320 -467
  115. package/src/sources/RpcSource.res.mjs +268 -385
  116. package/src/sources/RpcWebSocketHeightStream.res +0 -5
  117. package/src/sources/RpcWebSocketHeightStream.res.mjs +0 -2
  118. package/src/sources/SimulateSource.res +10 -4
  119. package/src/sources/SimulateSource.res.mjs +16 -6
  120. package/src/sources/Source.res +34 -6
  121. package/src/sources/SourceManager.res +77 -19
  122. package/src/sources/SourceManager.res.mjs +63 -21
  123. package/src/sources/SourceManager.resi +10 -0
  124. package/src/sources/Svm.res +31 -16
  125. package/src/sources/Svm.res.mjs +36 -13
  126. package/src/sources/SvmHyperSyncClient.res +5 -32
  127. package/src/sources/SvmHyperSyncSource.res +159 -134
  128. package/src/sources/SvmHyperSyncSource.res.mjs +164 -129
  129. package/src/sources/TransactionStore.res +49 -0
  130. package/src/sources/TransactionStore.res.mjs +30 -0
  131. package/src/tui/Tui.res +13 -16
  132. package/src/tui/Tui.res.mjs +12 -14
  133. package/svm.schema.json +49 -37
  134. package/src/bindings/Hrtime.res +0 -58
  135. package/src/bindings/Hrtime.res.mjs +0 -90
  136. package/src/bindings/Hrtime.resi +0 -30
@@ -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
  },
@@ -1,8 +1,47 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
3
  import * as Logging from "../Logging.res.mjs";
4
+ import * as Internal from "../Internal.res.mjs";
4
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";
5
9
  import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
10
+ import * as TransactionStore from "./TransactionStore.res.mjs";
11
+
12
+ let eventTransactionFieldMask = TransactionStore.makeMaskFn(Internal.allEvmTransactionFields);
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);
6
45
 
7
46
  let cleanUpRawEventFieldsInPlace = (fields => {
8
47
  delete fields.hash
@@ -13,82 +52,18 @@ let cleanUpRawEventFieldsInPlace = (fields => {
13
52
  function make(logger) {
14
53
  return {
15
54
  name: "evm",
16
- blockFields: [
17
- "number",
18
- "timestamp",
19
- "hash",
20
- "parentHash",
21
- "nonce",
22
- "sha3Uncles",
23
- "logsBloom",
24
- "transactionsRoot",
25
- "stateRoot",
26
- "receiptsRoot",
27
- "miner",
28
- "difficulty",
29
- "totalDifficulty",
30
- "extraData",
31
- "size",
32
- "gasLimit",
33
- "gasUsed",
34
- "uncles",
35
- "baseFeePerGas",
36
- "blobGasUsed",
37
- "excessBlobGas",
38
- "parentBeaconBlockRoot",
39
- "withdrawalsRoot",
40
- "l1BlockNumber",
41
- "sendCount",
42
- "sendRoot",
43
- "mixHash"
44
- ],
45
- transactionFields: [
46
- "transactionIndex",
47
- "hash",
48
- "from",
49
- "to",
50
- "gas",
51
- "gasPrice",
52
- "maxPriorityFeePerGas",
53
- "maxFeePerGas",
54
- "cumulativeGasUsed",
55
- "effectiveGasPrice",
56
- "gasUsed",
57
- "input",
58
- "nonce",
59
- "value",
60
- "v",
61
- "r",
62
- "s",
63
- "contractAddress",
64
- "logsBloom",
65
- "root",
66
- "status",
67
- "yParity",
68
- "chainId",
69
- "maxFeePerBlobGas",
70
- "blobVersionedHashes",
71
- "type",
72
- "l1Fee",
73
- "l1GasPrice",
74
- "l1GasUsed",
75
- "l1FeeScalar",
76
- "gasUsedForL1",
77
- "accessList",
78
- "authorizationList"
79
- ],
80
55
  blockNumberName: "number",
81
56
  blockTimestampName: "timestamp",
82
57
  blockHashName: "hash",
83
58
  cleanUpRawEventFieldsInPlace: cleanUpRawEventFieldsInPlace,
84
59
  onBlockMethodName: "onBlock",
85
60
  onBlockFilterSchema: S$RescriptSchema.object(s => s.f("block", S$RescriptSchema.option(S$RescriptSchema.object(s2 => s2.f("number", S$RescriptSchema.unknown))))),
86
- 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)))))),
87
62
  logger: logger,
88
63
  toEvent: eventItem => eventItem.payload,
89
64
  toEventLogger: eventItem => Logging.createChildFrom(logger, {
90
- contract: eventItem.eventConfig.contractName,
91
- event: eventItem.eventConfig.name,
65
+ contract: eventItem.onEventRegistration.eventConfig.contractName,
66
+ event: eventItem.onEventRegistration.eventConfig.name,
92
67
  chainId: eventItem.chain,
93
68
  block: eventItem.blockNumber,
94
69
  logIndex: eventItem.logIndex,
@@ -96,13 +71,22 @@ function make(logger) {
96
71
  }),
97
72
  toRawEvent: eventItem => {
98
73
  let payload = eventItem.payload;
99
- return RawEvent.make(eventItem, payload.block, payload.transaction, 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);
100
78
  }
101
79
  };
102
80
  }
103
81
 
82
+ let transactionFields = Internal.allEvmTransactionFields;
83
+
104
84
  export {
85
+ transactionFields,
86
+ eventTransactionFieldMask,
87
+ blockFields,
88
+ eventBlockFieldMask,
105
89
  cleanUpRawEventFieldsInPlace,
106
90
  make,
107
91
  }
108
- /* Logging Not a pure module */
92
+ /* eventTransactionFieldMask Not a pure module */
@@ -3,6 +3,7 @@ type rpc = {
3
3
  sourceFor: Source.sourceFor,
4
4
  syncConfig?: Config.sourceSyncOptions,
5
5
  ws?: string,
6
+ headers?: dict<string>,
6
7
  }
7
8
 
8
9
  let getSyncConfig = (
@@ -31,46 +32,42 @@ let getSyncConfig = (
31
32
  intervalCeiling: Env.Configurable.SyncConfig.intervalCeiling->Option.getOr(
32
33
  intervalCeiling->Option.getOr(10_000),
33
34
  ),
34
- backoffMillis: backoffMillis->Option.getOr(5000),
35
+ backoffMillis: backoffMillis->Option.getOr(2000),
35
36
  queryTimeoutMillis,
36
37
  fallbackStallTimeout: fallbackStallTimeout->Option.getOr(queryTimeoutMillis / 2),
37
38
  pollingInterval: pollingInterval->Option.getOr(1000),
38
39
  }
39
40
  }
40
41
 
41
- let collectEventParams = (contracts: array<Internal.evmContractConfig>): array<
42
+ let collectEventParams = (onEventRegistrations: array<Internal.evmOnEventRegistration>): array<
42
43
  HyperSyncClient.Decoder.eventParamsInput,
43
44
  > => {
44
45
  let result = []
45
- contracts->Array.forEach(contract => {
46
- contract.events->Array.forEach(event => {
47
- result
48
- ->Array.push({
49
- HyperSyncClient.Decoder.sighash: event.sighash,
50
- topicCount: event.topicCount,
51
- eventName: event.name,
52
- contractName: contract.name,
53
- params: event.paramsMetadata,
54
- })
55
- ->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,
56
55
  })
56
+ ->ignore
57
57
  })
58
58
  result
59
59
  }
60
60
 
61
61
  let makeSources = (
62
62
  ~chain,
63
- ~contracts: array<Internal.evmContractConfig>,
63
+ ~onEventRegistrations: array<Internal.evmOnEventRegistration>,
64
64
  ~hyperSync,
65
65
  ~rpcs: array<rpc>,
66
66
  ~lowercaseAddresses,
67
67
  ) => {
68
- let eventRouter =
69
- contracts
70
- ->Array.flatMap(contract => contract.events)
71
- ->EventRouter.fromEvmEventModsOrThrow(~chain)
68
+ let eventRouter = onEventRegistrations->EventRouter.fromEvmEventModsOrThrow(~chain)
72
69
 
73
- let allEventParams = collectEventParams(contracts)
70
+ let allEventParams = collectEventParams(onEventRegistrations)
74
71
 
75
72
  let sources = switch hyperSync {
76
73
  | Some(endpointUrl) => [
@@ -89,7 +86,7 @@ let makeSources = (
89
86
  ]
90
87
  | _ => []
91
88
  }
92
- rpcs->Array.forEach(({?syncConfig, url, sourceFor, ?ws}) => {
89
+ rpcs->Array.forEach(({?syncConfig, url, sourceFor, ?ws, ?headers}) => {
93
90
  let source = RpcSource.make({
94
91
  chain,
95
92
  sourceFor,
@@ -99,6 +96,7 @@ let makeSources = (
99
96
  allEventParams,
100
97
  lowercaseAddresses,
101
98
  ?ws,
99
+ ?headers,
102
100
  })
103
101
  let _ = sources->Array.push(source)
104
102
  })
@@ -13,32 +13,31 @@ function getSyncConfig(param) {
13
13
  backoffMultiplicative: Stdlib_Option.getOr(Env.Configurable.SyncConfig.backoffMultiplicative, Stdlib_Option.getOr(param.backoffMultiplicative, 0.8)),
14
14
  accelerationAdditive: Stdlib_Option.getOr(Env.Configurable.SyncConfig.accelerationAdditive, Stdlib_Option.getOr(param.accelerationAdditive, 500)),
15
15
  intervalCeiling: Stdlib_Option.getOr(Env.Configurable.SyncConfig.intervalCeiling, Stdlib_Option.getOr(param.intervalCeiling, 10000)),
16
- backoffMillis: Stdlib_Option.getOr(param.backoffMillis, 5000),
16
+ backoffMillis: Stdlib_Option.getOr(param.backoffMillis, 2000),
17
17
  queryTimeoutMillis: queryTimeoutMillis,
18
18
  fallbackStallTimeout: Stdlib_Option.getOr(param.fallbackStallTimeout, queryTimeoutMillis / 2 | 0),
19
19
  pollingInterval: Stdlib_Option.getOr(param.pollingInterval, 1000)
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,
@@ -60,7 +59,8 @@ function makeSources(chain, contracts, hyperSync, rpcs, lowercaseAddresses) {
60
59
  eventRouter: eventRouter,
61
60
  allEventParams: allEventParams,
62
61
  lowercaseAddresses: lowercaseAddresses,
63
- ws: param.ws
62
+ ws: param.ws,
63
+ headers: param.headers
64
64
  });
65
65
  sources.push(source);
66
66
  });
@@ -1,9 +1,56 @@
1
- type cfg = {url: string, httpReqTimeoutMillis?: int}
1
+ type cfg = {
2
+ url: string,
3
+ httpReqTimeoutMillis?: int,
4
+ headers?: dict<string>,
5
+ initialBlockInterval: int,
6
+ backoffMultiplicative: float,
7
+ accelerationAdditive: int,
8
+ intervalCeiling: int,
9
+ backoffMillis: int,
10
+ queryTimeoutMillis: int,
11
+ }
12
+
13
+ // Decoded `params` keyed by contract name, matching the HyperSync decoder's
14
+ // shape so the caller routes by address then picks its contract's params.
15
+ type rpcEventItem = {
16
+ log: Rpc.GetLogs.log,
17
+ params: Nullable.t<dict<Internal.eventParams>>,
18
+ }
19
+
20
+ // `addresses` omitted matches any address (a wildcard selection). Each `topics`
21
+ // position is `null` (match any) or a list of accepted topic hashes; the
22
+ // single-match case is a one-element list.
23
+ type logSelectionInput = {
24
+ addresses?: array<Address.t>,
25
+ topics: array<Nullable.t<array<string>>>,
26
+ }
2
27
 
3
- type t = {getHeight: unit => promise<int>}
28
+ type nextPageParams = {
29
+ fromBlock: int,
30
+ toBlockCeiling: int,
31
+ logSelections: array<logSelectionInput>,
32
+ partitionId: string,
33
+ }
34
+
35
+ type nextPageResponse = {
36
+ items: array<rpcEventItem>,
37
+ toBlock: int,
38
+ requestStats: array<Source.requestStat>,
39
+ }
40
+
41
+ // The caller provides a range; Rust decides the actual `toBlock` and returns it.
42
+ type t = {
43
+ getHeight: unit => promise<int>,
44
+ getNextPage: nextPageParams => promise<nextPageResponse>,
45
+ }
4
46
 
5
47
  @send
6
- external classNew: (Core.evmRpcClientCtor, cfg) => t = "new"
48
+ external classNew: (
49
+ Core.evmRpcClientCtor,
50
+ cfg,
51
+ array<HyperSyncClient.Decoder.eventParamsInput>,
52
+ ~checksumAddresses: bool,
53
+ ) => t = "new"
7
54
 
8
55
  // Rust encodes JSON-RPC errors as a JSON payload in the napi error's
9
56
  // message: `{"kind":"JsonRpcError","code":-32005,"message":"..."}`.
@@ -34,9 +81,31 @@ let coerceErrorOrThrow = exn =>
34
81
  | None => exn->throw
35
82
  }
36
83
 
37
- let make = (~url, ~httpReqTimeoutMillis=?) => {
38
- let client = Core.getAddon().evmRpcClient->classNew({url, ?httpReqTimeoutMillis})
84
+ let make = (
85
+ ~url,
86
+ ~checksumAddresses,
87
+ ~syncConfig: Config.sourceSync,
88
+ ~httpReqTimeoutMillis=?,
89
+ ~headers=?,
90
+ ~allEventParams=[],
91
+ ) => {
92
+ let client = Core.getAddon().evmRpcClient->classNew(
93
+ {
94
+ url,
95
+ ?httpReqTimeoutMillis,
96
+ ?headers,
97
+ initialBlockInterval: syncConfig.initialBlockInterval,
98
+ backoffMultiplicative: syncConfig.backoffMultiplicative,
99
+ accelerationAdditive: syncConfig.accelerationAdditive,
100
+ intervalCeiling: syncConfig.intervalCeiling,
101
+ backoffMillis: syncConfig.backoffMillis,
102
+ queryTimeoutMillis: syncConfig.queryTimeoutMillis,
103
+ },
104
+ allEventParams,
105
+ ~checksumAddresses,
106
+ )
39
107
  {
40
108
  getHeight: () => client.getHeight()->Promise.catch(coerceErrorOrThrow),
109
+ getNextPage: params => client.getNextPage(params)->Promise.catch(coerceErrorOrThrow),
41
110
  }
42
111
  }
@@ -46,13 +46,22 @@ function coerceErrorOrThrow(exn) {
46
46
  throw exn;
47
47
  }
48
48
 
49
- function make(url, httpReqTimeoutMillis) {
49
+ function make(url, checksumAddresses, syncConfig, httpReqTimeoutMillis, headers, allEventParamsOpt) {
50
+ let allEventParams = allEventParamsOpt !== undefined ? allEventParamsOpt : [];
50
51
  let client = Core.getAddon().EvmRpcClient.new({
51
52
  url: url,
52
- httpReqTimeoutMillis: httpReqTimeoutMillis
53
- });
53
+ httpReqTimeoutMillis: httpReqTimeoutMillis,
54
+ headers: headers,
55
+ initialBlockInterval: syncConfig.initialBlockInterval,
56
+ backoffMultiplicative: syncConfig.backoffMultiplicative,
57
+ accelerationAdditive: syncConfig.accelerationAdditive,
58
+ intervalCeiling: syncConfig.intervalCeiling,
59
+ backoffMillis: syncConfig.backoffMillis,
60
+ queryTimeoutMillis: syncConfig.queryTimeoutMillis
61
+ }, allEventParams, checksumAddresses);
54
62
  return {
55
- getHeight: () => Stdlib_Promise.$$catch(client.getHeight(), coerceErrorOrThrow)
63
+ getHeight: () => Stdlib_Promise.$$catch(client.getHeight(), coerceErrorOrThrow),
64
+ getNextPage: params => Stdlib_Promise.$$catch(client.getNextPage(params), coerceErrorOrThrow)
56
65
  };
57
66
  }
58
67
 
@@ -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`)