envio 3.13.0-alpha.0 → 3.13.0

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 (94) hide show
  1. package/package.json +6 -8
  2. package/src/BatchProcessing.res +4 -5
  3. package/src/BatchProcessing.res.mjs +4 -11
  4. package/src/ChainFetching.res +18 -38
  5. package/src/ChainFetching.res.mjs +14 -20
  6. package/src/ChainState.res +113 -77
  7. package/src/ChainState.res.mjs +90 -73
  8. package/src/ChainState.resi +14 -5
  9. package/src/Config.res +14 -17
  10. package/src/Config.res.mjs +5 -20
  11. package/src/CrossChainState.res +68 -20
  12. package/src/CrossChainState.res.mjs +48 -5
  13. package/src/CrossChainState.resi +1 -0
  14. package/src/Env.res +2 -2
  15. package/src/ErrorHandling.res +15 -4
  16. package/src/ErrorHandling.res.mjs +16 -5
  17. package/src/FinalizeBackfill.res +21 -9
  18. package/src/FinalizeBackfill.res.mjs +17 -4
  19. package/src/HandlerRegister.res +0 -37
  20. package/src/HandlerRegister.res.mjs +0 -18
  21. package/src/IndexerState.res +2 -0
  22. package/src/IndexerState.res.mjs +5 -0
  23. package/src/IndexerState.resi +1 -0
  24. package/src/Logging.res +6 -38
  25. package/src/Logging.res.mjs +5 -32
  26. package/src/Main.res +6 -9
  27. package/src/Main.res.mjs +3 -5
  28. package/src/Metrics.res +9 -4
  29. package/src/Metrics.res.mjs +3 -3
  30. package/src/Persistence.res +6 -3
  31. package/src/Persistence.res.mjs +5 -3
  32. package/src/PgStorage.res +2 -2
  33. package/src/PgStorage.res.mjs +2 -2
  34. package/src/Rollback.res +3 -3
  35. package/src/Server.res +0 -3
  36. package/src/SimulateItems.res +1 -1
  37. package/src/SimulateItems.res.mjs +1 -1
  38. package/src/Supervisor.res +236 -109
  39. package/src/Supervisor.res.mjs +185 -55
  40. package/src/TestIndexer.res +6 -3
  41. package/src/TestIndexer.res.mjs +3 -3
  42. package/src/Worker.res +6 -0
  43. package/src/Worker.res.mjs +2 -1
  44. package/src/bindings/NodeJs.res +25 -16
  45. package/src/bindings/NodeJs.res.mjs +8 -1
  46. package/src/sources/AddressSet.res +5 -3
  47. package/src/sources/AddressStore.res +19 -11
  48. package/src/sources/AddressStore.res.mjs +2 -7
  49. package/src/sources/BlockStore.res +10 -9
  50. package/src/sources/BlockStore.res.mjs +4 -4
  51. package/src/sources/ChainSources.res +4 -0
  52. package/src/sources/ChainSources.res.mjs +2 -2
  53. package/src/sources/EvmChain.res +4 -0
  54. package/src/sources/EvmChain.res.mjs +3 -1
  55. package/src/sources/EvmEventItem.res +56 -0
  56. package/src/sources/EvmEventItem.res.mjs +32 -0
  57. package/src/sources/EvmHyperSyncSource.res +7 -37
  58. package/src/sources/EvmHyperSyncSource.res.mjs +8 -29
  59. package/src/sources/EvmRpcClient.res +41 -59
  60. package/src/sources/EvmRpcClient.res.mjs +4 -54
  61. package/src/sources/EvmRpcWs.res +4 -1
  62. package/src/sources/EvmRpcWs.res.mjs +6 -2
  63. package/src/sources/FuelHyperSyncSource.res +6 -4
  64. package/src/sources/FuelHyperSyncSource.res.mjs +7 -9
  65. package/src/sources/HeightFeed.res +7 -1
  66. package/src/sources/HeightFeed.res.mjs +9 -7
  67. package/src/sources/HyperSyncClient.res +1 -14
  68. package/src/sources/RequestStat.res +7 -0
  69. package/src/sources/RequestStat.res.mjs +14 -1
  70. package/src/sources/RpcSource.res +100 -1043
  71. package/src/sources/RpcSource.res.mjs +81 -1056
  72. package/src/sources/SimulateSource.res +2 -5
  73. package/src/sources/SimulateSource.res.mjs +2 -3
  74. package/src/sources/Source.res +34 -12
  75. package/src/sources/Source.res.mjs +11 -0
  76. package/src/sources/SourceManager.res +111 -31
  77. package/src/sources/SourceManager.res.mjs +65 -31
  78. package/src/sources/SourceManager.resi +4 -0
  79. package/src/sources/StartBlockResolver.res +8 -5
  80. package/src/sources/StartBlockResolver.res.mjs +4 -2
  81. package/src/sources/SvmHyperSyncSource.res +4 -2
  82. package/src/sources/SvmHyperSyncSource.res.mjs +4 -6
  83. package/src/sources/TransactionStore.res +9 -6
  84. package/src/sources/TransactionStore.res.mjs +2 -2
  85. package/src/tui/Tui.res +4 -18
  86. package/src/tui/Tui.res.mjs +3 -6
  87. package/src/LazyLoader.res +0 -135
  88. package/src/LazyLoader.res.mjs +0 -118
  89. package/src/bindings/SDSL.res +0 -12
  90. package/src/bindings/SDSL.res.mjs +0 -9
  91. package/src/bindings/Yargs.res +0 -8
  92. package/src/bindings/Yargs.res.mjs +0 -2
  93. package/src/sources/Rpc.res +0 -185
  94. package/src/sources/Rpc.res.mjs +0 -183
@@ -12,16 +12,15 @@
12
12
  // blocks still inside the reorg threshold.
13
13
  type t
14
14
 
15
- @send external newEvm: (Core.blockStoreCtor, ~shouldChecksum: bool) => t = "newEvm"
15
+ @send external newEvm: Core.blockStoreCtor => t = "newEvm"
16
16
  @send external newSvm: Core.blockStoreCtor => t = "newSvm"
17
17
  @send external newFuel: Core.blockStoreCtor => t = "newFuel"
18
18
 
19
- // The store's ecosystem is fixed here, from the chain's config. EVM carries the
20
- // chain's address-checksumming setting; SVM/Fuel need no extra data.
21
- let make = (~ecosystem: Ecosystem.name, ~shouldChecksum: bool): t => {
19
+ // The store's ecosystem is fixed here, from the chain's config.
20
+ let make = (~ecosystem: Ecosystem.name): t => {
22
21
  let ctor = Core.getAddon().blockStore
23
22
  switch ecosystem {
24
- | Evm => ctor->newEvm(~shouldChecksum)
23
+ | Evm => ctor->newEvm
25
24
  | Svm => ctor->newSvm
26
25
  | Fuel => ctor->newFuel
27
26
  }
@@ -41,7 +40,7 @@ type svmBlockInput = {slot: int, hash?: string, time?: int}
41
40
  type fuelBlockInput = {height: int, id?: string, time?: int}
42
41
 
43
42
  @send
44
- external fromJsEvm: (Core.blockStoreCtor, array<evmBlockInput>, bool) => t = "fromJsEvm"
43
+ external fromJsEvm: (Core.blockStoreCtor, array<evmBlockInput>) => t = "fromJsEvm"
45
44
  @send
46
45
  external fromJsSvm: (Core.blockStoreCtor, array<svmBlockInput>) => t = "fromJsSvm"
47
46
  @send
@@ -53,7 +52,7 @@ type inputBlock = {blockNumber: int, blockHash?: string, blockTimestamp?: int}
53
52
 
54
53
  // Build a page from JS-observed blocks (RPC responses, stored reorg
55
54
  // checkpoints) for merging into the per-chain store.
56
- let fromJs = (blocks: array<inputBlock>, ~ecosystem: Ecosystem.name, ~shouldChecksum): t => {
55
+ let fromJs = (blocks: array<inputBlock>, ~ecosystem: Ecosystem.name): t => {
57
56
  let ctor = Core.getAddon().blockStore
58
57
  switch ecosystem {
59
58
  | Evm =>
@@ -63,7 +62,6 @@ let fromJs = (blocks: array<inputBlock>, ~ecosystem: Ecosystem.name, ~shouldChec
63
62
  hash: ?b.blockHash,
64
63
  timestamp: ?b.blockTimestamp,
65
64
  }),
66
- shouldChecksum,
67
65
  )
68
66
  | Svm =>
69
67
  ctor->fromJsSvm(
@@ -122,12 +120,15 @@ external latestValidBlockFromStore: (t, t, array<int>) => Null.t<int> = "latestV
122
120
 
123
121
  // Bulk-materialise blocks off the JS thread, one row per `blockNumbers[i]` key,
124
122
  // decoding only the fields set in that row's own `masks[i]`. Result is aligned
125
- // with the input.
123
+ // with the input. `shouldChecksum` is the caller's spelling for the addresses
124
+ // decoded out of the store: the rows themselves are raw bytes, so a page merged
125
+ // from a differently configured store can't fix it in.
126
126
  @send
127
127
  external materialize: (
128
128
  t,
129
129
  ~blockNumbers: array<int>,
130
130
  ~masks: array<float>,
131
+ ~shouldChecksum: bool,
131
132
  ) => promise<array<Internal.eventBlock>> = "materialize"
132
133
 
133
134
  // Drop blocks at or below the given block (already processed), keeping the
@@ -3,11 +3,11 @@
3
3
  import * as Core from "../Core.res.mjs";
4
4
  import * as FieldMask from "./FieldMask.res.mjs";
5
5
 
6
- function make(ecosystem, shouldChecksum) {
6
+ function make(ecosystem) {
7
7
  let ctor = Core.getAddon().BlockStore;
8
8
  switch (ecosystem) {
9
9
  case "evm" :
10
- return ctor.newEvm(shouldChecksum);
10
+ return ctor.newEvm();
11
11
  case "fuel" :
12
12
  return ctor.newFuel();
13
13
  case "svm" :
@@ -15,7 +15,7 @@ function make(ecosystem, shouldChecksum) {
15
15
  }
16
16
  }
17
17
 
18
- function fromJs(blocks, ecosystem, shouldChecksum) {
18
+ function fromJs(blocks, ecosystem) {
19
19
  let ctor = Core.getAddon().BlockStore;
20
20
  switch (ecosystem) {
21
21
  case "evm" :
@@ -23,7 +23,7 @@ function fromJs(blocks, ecosystem, shouldChecksum) {
23
23
  number: b.blockNumber,
24
24
  hash: b.blockHash,
25
25
  timestamp: b.blockTimestamp
26
- })), shouldChecksum);
26
+ })));
27
27
  case "fuel" :
28
28
  return ctor.fromJsFuel(blocks.map(b => ({
29
29
  height: b.blockNumber,
@@ -6,6 +6,8 @@ let make = (
6
6
  ~onEventRegistrations: array<Internal.onEventRegistration>,
7
7
  ~addressStore: AddressStore.t,
8
8
  ~lowercaseAddresses: bool,
9
+ ~blockStore: BlockStore.t,
10
+ ~transactionStore: TransactionStore.t,
9
11
  ): array<Source.t> => {
10
12
  let chainId = chainConfig.id
11
13
  switch chainConfig.sourceConfig {
@@ -31,6 +33,8 @@ let make = (
31
33
  ~rpcs=evmRpcs,
32
34
  ~lowercaseAddresses,
33
35
  ~addressStore,
36
+ ~blockStore,
37
+ ~transactionStore,
34
38
  )
35
39
  | Config.FuelSourceConfig({hypersync}) => [
36
40
  FuelHyperSyncSource.make({
@@ -7,7 +7,7 @@ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js
7
7
  import * as SvmHyperSyncSource from "./SvmHyperSyncSource.res.mjs";
8
8
  import * as FuelHyperSyncSource from "./FuelHyperSyncSource.res.mjs";
9
9
 
10
- function make(chainConfig, onEventRegistrations, addressStore, lowercaseAddresses) {
10
+ function make(chainConfig, onEventRegistrations, addressStore, lowercaseAddresses, blockStore, transactionStore) {
11
11
  let chainId = chainConfig.id;
12
12
  let sources = chainConfig.sourceConfig;
13
13
  switch (sources.TAG) {
@@ -24,7 +24,7 @@ function make(chainConfig, onEventRegistrations, addressStore, lowercaseAddresse
24
24
  headers: headers
25
25
  };
26
26
  });
27
- return EvmChain.makeSources(chainId, onEventRegistrations, sources.hypersync, evmRpcs, lowercaseAddresses, addressStore);
27
+ return EvmChain.makeSources(chainId, onEventRegistrations, sources.hypersync, evmRpcs, lowercaseAddresses, addressStore, blockStore, transactionStore);
28
28
  case "FuelSourceConfig" :
29
29
  return [FuelHyperSyncSource.make({
30
30
  chainId: chainId,
@@ -46,6 +46,8 @@ let makeSources = (
46
46
  ~rpcs: array<rpc>,
47
47
  ~lowercaseAddresses,
48
48
  ~addressStore,
49
+ ~blockStore,
50
+ ~transactionStore,
49
51
  ) => {
50
52
  let sources = switch hyperSync {
51
53
  | Some(endpointUrl) => [
@@ -73,6 +75,8 @@ let makeSources = (
73
75
  onEventRegistrations,
74
76
  lowercaseAddresses,
75
77
  addressStore,
78
+ blockStore,
79
+ transactionStore,
76
80
  ?ws,
77
81
  ?headers,
78
82
  })
@@ -19,7 +19,7 @@ function getSyncConfig(param) {
19
19
  };
20
20
  }
21
21
 
22
- function makeSources(chainId, onEventRegistrations, hyperSync, rpcs, lowercaseAddresses, addressStore) {
22
+ function makeSources(chainId, onEventRegistrations, hyperSync, rpcs, lowercaseAddresses, addressStore, blockStore, transactionStore) {
23
23
  let sources = hyperSync !== undefined ? [EvmHyperSyncSource.make({
24
24
  chainId: chainId,
25
25
  endpointUrl: hyperSync,
@@ -41,6 +41,8 @@ function makeSources(chainId, onEventRegistrations, hyperSync, rpcs, lowercaseAd
41
41
  onEventRegistrations: onEventRegistrations,
42
42
  lowercaseAddresses: lowercaseAddresses,
43
43
  addressStore: addressStore,
44
+ blockStore: blockStore,
45
+ transactionStore: transactionStore,
44
46
  ws: param.ws,
45
47
  headers: param.headers
46
48
  });
@@ -0,0 +1,56 @@
1
+ // A decoded EVM log that routed to a registration, as both EVM sources return
2
+ // it. Everything else the log carried — its topics, its transaction hash, its
3
+ // block hash — is consumed on the Rust side, so what crosses the boundary is
4
+ // what an item is actually built from.
5
+ type t = {
6
+ logIndex: int,
7
+ srcAddress: Address.t,
8
+ // Number of the block this log belongs to; the block itself is resolved from
9
+ // the chain's block store, deduplicated across items sharing a block.
10
+ blockNumber: int,
11
+ // Key (with the block number) into the transaction store; the transaction
12
+ // is resolved from the store on demand.
13
+ transactionIndex: int,
14
+ // The registration this log routed to, by chain-scoped index. Logs that
15
+ // route to no registration never cross the boundary.
16
+ onEventRegistrationIndex: int,
17
+ params: Internal.eventParams,
18
+ }
19
+
20
+ // `block` and `transaction` are omitted from the payload: both sources leave
21
+ // them to be materialised from the per-chain stores at batch prep.
22
+ let toInternalItem = (
23
+ item: t,
24
+ ~onEventRegistration: Internal.evmOnEventRegistration,
25
+ ~chainId,
26
+ ): Internal.item => {
27
+ let {transactionIndex, logIndex, srcAddress, blockNumber, params} = item
28
+
29
+ Internal.Event({
30
+ onEventRegistration: (onEventRegistration :> Internal.onEventRegistration),
31
+ chainId,
32
+ blockNumber,
33
+ logIndex,
34
+ transactionIndex,
35
+ payload: {
36
+ contractName: onEventRegistration.eventConfig.contractName,
37
+ eventName: onEventRegistration.eventConfig.name,
38
+ chainId,
39
+ params,
40
+ srcAddress,
41
+ logIndex,
42
+ }->Evm.fromPayload,
43
+ })
44
+ }
45
+
46
+ let toInternalItems = (
47
+ items: array<t>,
48
+ ~onEventRegistrations: array<Internal.evmOnEventRegistration>,
49
+ ~chainId,
50
+ ): array<Internal.item> =>
51
+ items->Array.map(item =>
52
+ item->toInternalItem(
53
+ ~onEventRegistration=onEventRegistrations->Array.getUnsafe(item.onEventRegistrationIndex),
54
+ ~chainId,
55
+ )
56
+ )
@@ -0,0 +1,32 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+
4
+ function toInternalItem(item, onEventRegistration, chainId) {
5
+ let logIndex = item.logIndex;
6
+ return {
7
+ kind: 0,
8
+ onEventRegistration: onEventRegistration,
9
+ chainId: chainId,
10
+ blockNumber: item.blockNumber,
11
+ logIndex: logIndex,
12
+ transactionIndex: item.transactionIndex,
13
+ payload: {
14
+ contractName: onEventRegistration.eventConfig.contractName,
15
+ eventName: onEventRegistration.eventConfig.name,
16
+ params: item.params,
17
+ chainId: chainId,
18
+ srcAddress: item.srcAddress,
19
+ logIndex: logIndex
20
+ }
21
+ };
22
+ }
23
+
24
+ function toInternalItems(items, onEventRegistrations, chainId) {
25
+ return items.map(item => toInternalItem(item, onEventRegistrations[item.onEventRegistrationIndex], chainId));
26
+ }
27
+
28
+ export {
29
+ toInternalItem,
30
+ toInternalItems,
31
+ }
32
+ /* No side effect */
@@ -55,31 +55,6 @@ let make = (
55
55
  )
56
56
  }
57
57
 
58
- let makeEventBatchQueueItem = (
59
- item: HyperSyncClient.EventItems.item,
60
- ~onEventRegistration: Internal.evmOnEventRegistration,
61
- ): Internal.item => {
62
- let {transactionIndex, logIndex, srcAddress} = item
63
-
64
- Internal.Event({
65
- onEventRegistration: (onEventRegistration :> Internal.onEventRegistration),
66
- chainId,
67
- blockNumber: item.blockNumber,
68
- logIndex,
69
- transactionIndex,
70
- // `block` and `transaction` are omitted; they're materialised from the
71
- // per-chain stores onto the payload at batch prep.
72
- payload: {
73
- contractName: onEventRegistration.eventConfig.contractName,
74
- eventName: onEventRegistration.eventConfig.name,
75
- chainId,
76
- params: item.params,
77
- srcAddress,
78
- logIndex,
79
- }->Evm.fromPayload,
80
- })
81
- }
82
-
83
58
  let getItemsOrThrow = async (
84
59
  ~fromBlock,
85
60
  ~toBlock,
@@ -96,6 +71,8 @@ let make = (
96
71
 
97
72
  let startFetchingBatchTimeRef = Performance.now()
98
73
 
74
+ let fetchStats = () => RequestStat.single(~method="getLogs", ~sentAt=startFetchingBatchTimeRef)
75
+
99
76
  //fetch batch
100
77
  let pageUnsafe = try await HyperSync.GetLogs.query(
101
78
  ~client,
@@ -108,12 +85,12 @@ let make = (
108
85
  ~includeAllBlocks,
109
86
  ) catch {
110
87
  | HyperSync.GetLogs.Error(WrongInstance) =>
111
- throw(Source.SourceBehindHead({blockNumber: fromBlock, requestStats: []}))
88
+ throw(Source.SourceBehindHead({blockNumber: fromBlock, requestStats: fetchStats()}))
112
89
  | HyperSync.GetLogs.Error(UnexpectedMissingParams({missingParams})) =>
113
90
  throw(
114
91
  Source.GetItemsError(
115
92
  Source.FailedGettingItems({
116
- exn: %raw(`null`),
93
+ requestStats: fetchStats(),
117
94
  attemptedToBlock: toBlock->Option.getOr(knownHeight),
118
95
  retry: ImpossibleForTheQuery({
119
96
  message: `Source returned invalid data with missing required fields: ${missingParams->Array.joinUnsafe(
@@ -128,6 +105,7 @@ let make = (
128
105
  throw(
129
106
  Source.GetItemsError(
130
107
  Source.FailedGettingItems({
108
+ requestStats: fetchStats(),
131
109
  exn,
132
110
  attemptedToBlock: toBlock->Option.getOr(knownHeight),
133
111
  retry: WithBackoff({
@@ -157,15 +135,8 @@ let make = (
157
135
 
158
136
  let parsingTimeRef = Performance.now()
159
137
 
160
- //Parse page items into queue items
161
- let parsedQueueItems = []
162
-
163
- pageUnsafe.items->Array.forEach(item => {
164
- let onEventRegistration = onEventRegistrations->Array.getUnsafe(item.onEventRegistrationIndex)
165
- parsedQueueItems
166
- ->Array.push(makeEventBatchQueueItem(item, ~onEventRegistration))
167
- ->ignore
168
- })
138
+ let parsedQueueItems =
139
+ pageUnsafe.items->EvmEventItem.toInternalItems(~onEventRegistrations, ~chainId)
169
140
 
170
141
  let parsingTimeElapsed = parsingTimeRef->Performance.secondsSince
171
142
 
@@ -186,7 +157,6 @@ let make = (
186
157
  latestFetchedBlockNumber: heighestBlockQueried,
187
158
  stats,
188
159
  knownHeight,
189
- fromBlockQueried: fromBlock,
190
160
  requestStats,
191
161
  }
192
162
  }
@@ -3,6 +3,8 @@
3
3
  import * as Source from "./Source.res.mjs";
4
4
  import * as HyperSync from "./HyperSync.res.mjs";
5
5
  import * as Performance from "../bindings/Performance.res.mjs";
6
+ import * as RequestStat from "./RequestStat.res.mjs";
7
+ import * as EvmEventItem from "./EvmEventItem.res.mjs";
6
8
  import * as HyperSyncSSE from "./HyperSyncSSE.res.mjs";
7
9
  import * as ErrorHandling from "../ErrorHandling.res.mjs";
8
10
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
@@ -25,25 +27,6 @@ function make(param) {
25
27
  let exn = Primitive_exceptions.internalToException(raw_exn);
26
28
  client = ErrorHandling.mkLogAndRaise(undefined, "Failed to instantiate the hypersync client, please double check your ABI", exn);
27
29
  }
28
- let makeEventBatchQueueItem = (item, onEventRegistration) => {
29
- let logIndex = item.logIndex;
30
- return {
31
- kind: 0,
32
- onEventRegistration: onEventRegistration,
33
- chainId: chainId,
34
- blockNumber: item.blockNumber,
35
- logIndex: logIndex,
36
- transactionIndex: item.transactionIndex,
37
- payload: {
38
- contractName: onEventRegistration.eventConfig.contractName,
39
- eventName: onEventRegistration.eventConfig.name,
40
- params: item.params,
41
- chainId: chainId,
42
- srcAddress: item.srcAddress,
43
- logIndex: logIndex
44
- }
45
- };
46
- };
47
30
  let getItemsOrThrow = async (fromBlock, toBlock, addressSet, includeAllBlocks, knownHeight, param, selection, itemsTarget, retry, param$1) => {
48
31
  let totalTimeRef = Performance.now();
49
32
  let startFetchingBatchTimeRef = Performance.now();
@@ -58,7 +41,7 @@ function make(param) {
58
41
  throw {
59
42
  RE_EXN_ID: Source.SourceBehindHead,
60
43
  blockNumber: fromBlock,
61
- requestStats: [],
44
+ requestStats: RequestStat.single("getLogs", startFetchingBatchTimeRef),
62
45
  Error: new Error()
63
46
  };
64
47
  }
@@ -66,12 +49,12 @@ function make(param) {
66
49
  RE_EXN_ID: Source.GetItemsError,
67
50
  _1: {
68
51
  TAG: "FailedGettingItems",
69
- exn: null,
70
52
  attemptedToBlock: Stdlib_Option.getOr(toBlock, knownHeight),
71
53
  retry: {
72
54
  TAG: "ImpossibleForTheQuery",
73
55
  message: `Source returned invalid data with missing required fields: ` + match.missingParams.join(", ")
74
- }
56
+ },
57
+ requestStats: RequestStat.single("getLogs", startFetchingBatchTimeRef)
75
58
  },
76
59
  Error: new Error()
77
60
  };
@@ -92,7 +75,8 @@ function make(param) {
92
75
  TAG: "WithBackoff",
93
76
  message: `Unexpected issue while fetching events from HyperSync client. Attempt a retry.`,
94
77
  backoffMillis: retry !== 0 ? 1000 * retry | 0 : 500
95
- }
78
+ },
79
+ requestStats: RequestStat.single("getLogs", startFetchingBatchTimeRef)
96
80
  },
97
81
  Error: new Error()
98
82
  };
@@ -106,11 +90,7 @@ function make(param) {
106
90
  let knownHeight$1 = pageUnsafe.archiveHeight;
107
91
  let heighestBlockQueried = pageUnsafe.nextBlock - 1 | 0;
108
92
  let parsingTimeRef = Performance.now();
109
- let parsedQueueItems = [];
110
- pageUnsafe.items.forEach(item => {
111
- let onEventRegistration = onEventRegistrations[item.onEventRegistrationIndex];
112
- parsedQueueItems.push(makeEventBatchQueueItem(item, onEventRegistration));
113
- });
93
+ let parsedQueueItems = EvmEventItem.toInternalItems(pageUnsafe.items, onEventRegistrations, chainId);
114
94
  let parsingTimeElapsed = Performance.secondsSince(parsingTimeRef);
115
95
  let totalTimeElapsed = Performance.secondsSince(totalTimeRef);
116
96
  let stats_parsing$unknowntime$unknown$lpars$rpar = parsingTimeElapsed;
@@ -125,7 +105,6 @@ function make(param) {
125
105
  parsedQueueItems: parsedQueueItems,
126
106
  transactionStore: Primitive_option.some(pageUnsafe.transactionStore),
127
107
  blockStore: pageUnsafe.blockStore,
128
- fromBlockQueried: fromBlock,
129
108
  latestFetchedBlockNumber: heighestBlockQueried,
130
109
  stats: stats,
131
110
  requestStats: requestStats
@@ -1,6 +1,6 @@
1
1
  type cfg = {
2
2
  url: string,
3
- httpReqTimeoutMillis?: int,
3
+ maxConcurrentRequests?: int,
4
4
  headers?: dict<string>,
5
5
  initialBlockInterval: int,
6
6
  backoffMultiplicative: float,
@@ -10,38 +10,55 @@ type cfg = {
10
10
  queryTimeoutMillis: int,
11
11
  }
12
12
 
13
- // Only logs that resolved to a registration cross the boundary, each carrying
14
- // its registration's chain-scoped index.
15
- type rpcEventItem = {
16
- log: Rpc.GetLogs.log,
17
- onEventRegistrationIndex: int,
18
- params: Internal.eventParams,
19
- }
20
-
21
13
  type nextPageParams = {
22
14
  fromBlock: int,
23
15
  toBlockCeiling: int,
24
16
  partitionId: string,
25
- // The partition's registration selection, by chain-scoped index. Log
26
- // selections and the routing index are derived on the Rust side from the
27
- // registrations passed at construction.
28
17
  registrationIndexes: array<int>,
29
- // Contract names to fetch address-free even though their registrations
30
- // depend on addresses (client-side filtering). None/empty means
31
- // every address-dependent contract is filtered server-side.
32
18
  clientFilteredContracts: option<array<string>>,
19
+ retry: int,
33
20
  }
34
21
 
35
- type nextPageResponse = {
36
- items: array<rpcEventItem>,
22
+ // Rust reports what to do about a page it could not read rather than throwing
23
+ // it; see `NextPageResult` in `evm_rpc_source/mod.rs` for which fields each
24
+ // `kind` populates. A thrown error from `getNextPage` is a genuine bug, not an
25
+ // outcome to recover from.
26
+ type pageOutcome =
27
+ | @as("Ok") Ok
28
+ | @as("SuggestedToBlock") SuggestedToBlock
29
+ | @as("Backoff") Backoff
30
+ | @as("FieldSelection") FieldSelection
31
+
32
+ type nextPageResult = {
33
+ kind: pageOutcome,
34
+ requestStats: array<Source.requestStat>,
37
35
  toBlock: int,
36
+ items: array<EvmEventItem.t>,
37
+ message: option<string>,
38
+ providerMessage: option<string>,
39
+ blockNumber: option<int>,
40
+ retryToBlock: option<int>,
41
+ backoffMillis: option<int>,
42
+ }
43
+
44
+ // `message` is set when the read failed, in which case the page returned
45
+ // alongside it is empty.
46
+ type blockHashResult = {
47
+ message: option<string>,
38
48
  requestStats: array<Source.requestStat>,
39
49
  }
40
50
 
41
- // The caller provides a range; Rust decides the actual `toBlock` and returns it.
42
51
  type t = {
43
- getHeight: unit => promise<int>,
44
- getNextPage: (nextPageParams, AddressSet.t) => promise<nextPageResponse>,
52
+ getHeight: unit => promise<(int, array<Source.requestStat>)>,
53
+ // The stores it returns are this page's own, for the caller to merge.
54
+ getNextPage: (
55
+ nextPageParams,
56
+ AddressSet.t,
57
+ BlockStore.t,
58
+ TransactionStore.t,
59
+ ) => promise<(nextPageResult, BlockStore.t, TransactionStore.t)>,
60
+ getBlockHashes: array<int> => promise<(blockHashResult, BlockStore.t)>,
61
+ onReorg: unit => unit,
45
62
  }
46
63
 
47
64
  @send
@@ -53,48 +70,19 @@ external classNew: (
53
70
  ~addressStore: AddressStore.t,
54
71
  ) => t = "new"
55
72
 
56
- // Rust encodes JSON-RPC errors as a JSON payload in the napi error's
57
- // message: `{"kind":"JsonRpcError","code":-32005,"message":"..."}`.
58
- // Parse it back so callers keep matching on Rpc.JsonRpcError.
59
- let getJsonRpcError = (exn: exn): option<Rpc.rpcError> =>
60
- switch exn {
61
- | JsExn(e) =>
62
- switch e->JsExn.message {
63
- | Some(msg) =>
64
- switch msg->JSON.parseOrThrow->JSON.Decode.object {
65
- | exception _ => None
66
- | None => None
67
- | Some(obj) =>
68
- switch (obj->Dict.get("kind"), obj->Dict.get("code"), obj->Dict.get("message")) {
69
- | (Some(String("JsonRpcError")), Some(Number(code)), Some(String(message))) =>
70
- Some({code: code->Float.toInt, message})
71
- | _ => None
72
- }
73
- }
74
- | None => None
75
- }
76
- | _ => None
77
- }
78
-
79
- let coerceErrorOrThrow = exn =>
80
- switch exn->getJsonRpcError {
81
- | Some(rpcError) => throw(Rpc.JsonRpcError(rpcError))
82
- | None => exn->throw
83
- }
84
-
85
73
  let make = (
86
74
  ~url,
87
75
  ~checksumAddresses,
88
76
  ~syncConfig: Config.sourceSync,
89
- ~httpReqTimeoutMillis=?,
77
+ ~maxConcurrentRequests=?,
90
78
  ~headers=?,
91
79
  ~eventRegistrations=[],
92
80
  ~addressStore,
93
- ) => {
94
- let client = Core.getAddon().evmRpcClient->classNew(
81
+ ) =>
82
+ Core.getAddon().evmRpcClient->classNew(
95
83
  {
96
84
  url,
97
- ?httpReqTimeoutMillis,
85
+ ?maxConcurrentRequests,
98
86
  ?headers,
99
87
  initialBlockInterval: syncConfig.initialBlockInterval,
100
88
  backoffMultiplicative: syncConfig.backoffMultiplicative,
@@ -107,9 +95,3 @@ let make = (
107
95
  ~checksumAddresses,
108
96
  ~addressStore,
109
97
  )
110
- {
111
- getHeight: () => client.getHeight()->Promise.catch(coerceErrorOrThrow),
112
- getNextPage: (params, addressSet) =>
113
- client.getNextPage(params, addressSet)->Promise.catch(coerceErrorOrThrow),
114
- }
115
- }
@@ -1,56 +1,12 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
- import * as Rpc from "./Rpc.res.mjs";
4
3
  import * as Core from "../Core.res.mjs";
5
- import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
6
- import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
7
- import * as Stdlib_Promise from "@rescript/runtime/lib/es6/Stdlib_Promise.js";
8
4
 
9
- function getJsonRpcError(exn) {
10
- if (exn.RE_EXN_ID !== "JsExn") {
11
- return;
12
- }
13
- let msg = Stdlib_JsExn.message(exn._1);
14
- if (msg === undefined) {
15
- return;
16
- }
17
- let obj;
18
- try {
19
- obj = Stdlib_JSON.Decode.object(JSON.parse(msg));
20
- } catch (exn$1) {
21
- return;
22
- }
23
- if (obj === undefined) {
24
- return;
25
- }
26
- let match = obj["kind"];
27
- let match$1 = obj["code"];
28
- let match$2 = obj["message"];
29
- if (match === "JsonRpcError" && typeof match$1 === "number" && typeof match$2 === "string") {
30
- return {
31
- code: match$1 | 0,
32
- message: match$2
33
- };
34
- }
35
- }
36
-
37
- function coerceErrorOrThrow(exn) {
38
- let rpcError = getJsonRpcError(exn);
39
- if (rpcError !== undefined) {
40
- throw {
41
- RE_EXN_ID: Rpc.JsonRpcError,
42
- _1: rpcError,
43
- Error: new Error()
44
- };
45
- }
46
- throw exn;
47
- }
48
-
49
- function make(url, checksumAddresses, syncConfig, httpReqTimeoutMillis, headers, eventRegistrationsOpt, addressStore) {
5
+ function make(url, checksumAddresses, syncConfig, maxConcurrentRequests, headers, eventRegistrationsOpt, addressStore) {
50
6
  let eventRegistrations = eventRegistrationsOpt !== undefined ? eventRegistrationsOpt : [];
51
- let client = Core.getAddon().EvmRpcClient.new({
7
+ return Core.getAddon().EvmRpcClient.new({
52
8
  url: url,
53
- httpReqTimeoutMillis: httpReqTimeoutMillis,
9
+ maxConcurrentRequests: maxConcurrentRequests,
54
10
  headers: headers,
55
11
  initialBlockInterval: syncConfig.initialBlockInterval,
56
12
  backoffMultiplicative: syncConfig.backoffMultiplicative,
@@ -59,15 +15,9 @@ function make(url, checksumAddresses, syncConfig, httpReqTimeoutMillis, headers,
59
15
  backoffMillis: syncConfig.backoffMillis,
60
16
  queryTimeoutMillis: syncConfig.queryTimeoutMillis
61
17
  }, eventRegistrations, checksumAddresses, addressStore);
62
- return {
63
- getHeight: () => Stdlib_Promise.$$catch(client.getHeight(), coerceErrorOrThrow),
64
- getNextPage: (params, addressSet) => Stdlib_Promise.$$catch(client.getNextPage(params, addressSet), coerceErrorOrThrow)
65
- };
66
18
  }
67
19
 
68
20
  export {
69
- getJsonRpcError,
70
- coerceErrorOrThrow,
71
21
  make,
72
22
  }
73
- /* Rpc Not a pure module */
23
+ /* Core Not a pure module */
@@ -43,6 +43,9 @@ let refusesSubscribe = (id, ~subscribed) =>
43
43
  | Some(id) => id === subscribeRequestIdText
44
44
  }
45
45
 
46
+ external number: string => int = "Number"
47
+ let hexIntSchema: S.schema<int> = S.string->S.transform(_ => {parser: str => str->number})
48
+
46
49
  let wsMessageSchema = S.union([
47
50
  S.object(s => {
48
51
  let _ = s.field("method", S.literal("eth_subscription"))
@@ -54,7 +57,7 @@ let wsMessageSchema = S.union([
54
57
  "result",
55
58
  S.object(
56
59
  s => {
57
- s.field("number", Rpc.hexIntSchema)
60
+ s.field("number", hexIntSchema)
58
61
  },
59
62
  ),
60
63
  )