envio 3.3.0-alpha.10 → 3.3.0-alpha.12

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 (60) hide show
  1. package/licenses/CLA.md +35 -0
  2. package/licenses/EULA.md +67 -0
  3. package/licenses/LICENSE.md +45 -0
  4. package/licenses/README.md +35 -0
  5. package/package.json +9 -8
  6. package/src/Batch.res.mjs +1 -1
  7. package/src/ChainFetching.res +30 -18
  8. package/src/ChainFetching.res.mjs +23 -13
  9. package/src/ChainState.res +45 -30
  10. package/src/ChainState.res.mjs +22 -8
  11. package/src/ChainState.resi +5 -0
  12. package/src/Config.res +9 -5
  13. package/src/Config.res.mjs +2 -2
  14. package/src/Core.res +20 -0
  15. package/src/Core.res.mjs +12 -0
  16. package/src/CrossChainState.res +64 -29
  17. package/src/CrossChainState.res.mjs +17 -8
  18. package/src/EventConfigBuilder.res +21 -46
  19. package/src/EventConfigBuilder.res.mjs +18 -25
  20. package/src/EventProcessing.res +8 -5
  21. package/src/EventProcessing.res.mjs +2 -1
  22. package/src/FetchState.res +428 -322
  23. package/src/FetchState.res.mjs +316 -233
  24. package/src/HandlerRegister.res +3 -1
  25. package/src/HandlerRegister.res.mjs +3 -2
  26. package/src/Internal.res +11 -2
  27. package/src/LogSelection.res +0 -62
  28. package/src/LogSelection.res.mjs +0 -74
  29. package/src/RawEvent.res +2 -2
  30. package/src/Rollback.res +32 -13
  31. package/src/Rollback.res.mjs +24 -11
  32. package/src/SimulateDeadInputTracker.res +1 -1
  33. package/src/SimulateItems.res +38 -6
  34. package/src/SimulateItems.res.mjs +28 -9
  35. package/src/TestIndexer.res +2 -2
  36. package/src/TestIndexer.res.mjs +2 -2
  37. package/src/TopicFilter.res +1 -25
  38. package/src/TopicFilter.res.mjs +0 -74
  39. package/src/bindings/Viem.res +0 -5
  40. package/src/sources/EventRouter.res +0 -21
  41. package/src/sources/EventRouter.res.mjs +0 -12
  42. package/src/sources/EvmChain.res +2 -27
  43. package/src/sources/EvmChain.res.mjs +2 -23
  44. package/src/sources/EvmRpcClient.res +12 -15
  45. package/src/sources/EvmRpcClient.res.mjs +3 -3
  46. package/src/sources/HyperSync.res +9 -38
  47. package/src/sources/HyperSync.res.mjs +16 -28
  48. package/src/sources/HyperSync.resi +2 -2
  49. package/src/sources/HyperSyncClient.res +88 -11
  50. package/src/sources/HyperSyncClient.res.mjs +39 -6
  51. package/src/sources/HyperSyncSource.res +18 -199
  52. package/src/sources/HyperSyncSource.res.mjs +9 -128
  53. package/src/sources/Rpc.res +0 -32
  54. package/src/sources/Rpc.res.mjs +1 -46
  55. package/src/sources/RpcSource.res +29 -175
  56. package/src/sources/RpcSource.res.mjs +32 -110
  57. package/src/sources/SimulateSource.res +37 -19
  58. package/src/sources/SimulateSource.res.mjs +27 -10
  59. package/src/sources/SvmHyperSyncSource.res +13 -3
  60. package/src/sources/SvmHyperSyncSource.res.mjs +1 -1
@@ -0,0 +1,35 @@
1
+ # Contributor License Agreement (CLA)
2
+
3
+ ## 1. Introduction
4
+
5
+ Thank you for your interest in contributing to this project ("Project"). We value your time and effort, and we appreciate all contributions made by the community. By submitting any work (a "Contribution"), you agree to the terms below.
6
+
7
+ ## 2. Automatic Acceptance
8
+
9
+ By submitting a Pull Request or making any contribution to this project, you automatically agree to and accept all terms outlined in this Contributor License Agreement. This includes all future contributions you may make to the project.
10
+
11
+ ## 3. Your Contributions
12
+
13
+ 3.1 A "Contribution" includes any original work you share with the Project, such as code, documentation, or other creative content.
14
+
15
+ 3.2 You confirm that each Contribution is either your own original work or that you have all necessary rights and permissions to submit it under this CLA.
16
+
17
+ ## 4. License Grant
18
+
19
+ 4.1 By submitting a Contribution, you grant Envio (the "Maintainer") an irrevocable, worldwide, non-exclusive, royalty-free, perpetual license to use, reproduce, modify, distribute, prepare derivative works of, publicly display, perform, sublicense, and otherwise exploit your Contribution as part of the Project.
20
+
21
+ 4.2 You agree that the Maintainer may license or re-license your Contribution under terms compatible with those of the Project.
22
+
23
+ ## 5. Ongoing Use
24
+
25
+ 5.1 Once provided, Contributions cannot be retracted; however, the Maintainer is not obligated to include or continue using your Contribution.
26
+
27
+ 5.2 You understand that this CLA helps ensure your work remains compatible with the Project's licensing, now and in the future.
28
+
29
+ ## 6. Disclaimer
30
+
31
+ 6.1 You provide your Contributions on an "as is" basis, without warranties of any kind. This includes, but is not limited to, warranties of merchantability, fitness for a particular purpose, and non-infringement.
32
+
33
+ ## 7. Governing Law
34
+
35
+ 7.1 This CLA is governed by and construed in accordance with the laws of England. Any disputes will be subject to the exclusive jurisdiction of the English courts.
@@ -0,0 +1,67 @@
1
+ ## HyperIndex End-User License Agreement (EULA)
2
+
3
+ This agreement describes the users' rights and the conditions upon which the Software and Generated Code may be used. The user should review the entire agreement, including any supplemental license terms that accompany the Software, since all of the terms are important and together create this agreement that applies to them.
4
+
5
+ ### 1. Definitions
6
+
7
+ **Software:** HyperIndex, a copyrightable work created by Envio and licensed under this End User License Agreement (“EULA”).
8
+
9
+ **Generated Code:** In the context of this license agreement, the term "generated code" refers to computer programming code that is produced automatically by the Software based on input provided by the user.
10
+
11
+ **Licensed Material:** The Software and Generated Code defined here will be collectively referred to as “Licensed Material”.
12
+
13
+ ### 2. Installation and User Rights
14
+
15
+ **License:** The Software is provided under this EULA. By agreeing to the EULA terms, you are granted the right to install and operate one instance of the Software on your device (referred to as the licensed device), for the use of one individual at a time, on the condition that you adhere to all terms outlined in this agreement.
16
+ The licensor provides you with a non-exclusive, royalty-free, worldwide license that is non-sublicensable and non-transferable. This license allows you to use the Software subject to the limitations and conditions outlined in this EULA.
17
+ With one license, the user can only use the Software on a single device.
18
+
19
+ **Device:** In this agreement, "device" refers to a hardware system, whether physical or virtual, equipped with an internal storage device capable of executing the Software. This includes hardware partitions, which are considered as individual devices for the purposes of this agreement. Updates may be provided to the Software, and these updates may alter the minimum hardware requirements necessary for the Software. It is the responsibility of users to comply with any changing hardware requirements.
20
+
21
+ **Updates:** The Software may be updated automatically. With each update, the EULA may be amended, and it is the users' responsibility to comply with the amendments.
22
+
23
+ **Limitations:** Envio reserves all rights, including those under intellectual property laws, not expressly granted in this agreement. For instance, this license does not confer upon you the right to, and you are prohibited from:
24
+
25
+ (i) Publishing, copying (other than the permitted backup copy), renting, leasing, or lending the Software;
26
+
27
+ (ii) Transferring the Software (except as permitted by this agreement);
28
+
29
+ (iii) Circumventing any technical restrictions or limitations in the Software;
30
+
31
+ (iv) Using the Software as server Software, for commercial hosting, making the Software available for simultaneous use by multiple users over a network, installing the Software on a server and allowing users to access it remotely, or installing the Software on a device solely for remote user use;
32
+
33
+ (v) Reverse engineering, decompiling, or disassembling the Software, or attempting to do so, except and only to the extent that the foregoing restriction is (a) permitted by applicable law; (b) permitted by licensing terms governing the use of open-source components that may be included with the Software and
34
+
35
+ (vi) When using the Software, you may not use any features in any manner that could interfere with anyone else's use of them, or attempt to gain unauthorized access to or use of any service, data, account, or network.
36
+
37
+ These limitations apply specifically to the Software and do not extend to the Generated Code. Details regarding the use of the Generated Code, including associated limitations, are provided below.
38
+
39
+ ### 3. Use of the Generated Code
40
+
41
+ **Limitations:** Users can use, copy, distribute, make available, and create derivative works of the Generated Code freely, subject to the limitations and conditions specified below.
42
+
43
+ (i) The user is prohibited from offering the Generated Code or any software that includes the Generated Code to third parties as a hosted or managed service, where the service grants users access to a significant portion of the Software's features or functionality.
44
+
45
+ (ii) The user is not permitted to tamper with, alter, disable, or bypass the functionality of the license key in the Software. Additionally, the user may not eliminate or conceal any functionality within the Software that is safeguarded by the license key.
46
+
47
+ (iii) Any modification, removal, or concealment of licensing, copyright, or other notices belonging to the licensor in the Software is strictly forbidden. The use of the licensor's trademarks is subject to relevant laws.
48
+
49
+ **Credit:** If the user utilizes the Generated Code to develop and release a new software, product or service, the license agreement for said software, product or service must include proper credit to HyperIndex.
50
+
51
+ **Liability:** Envio does not provide any assurance that the Generated Code functions correctly, nor does it assume any responsibility in this regard.
52
+
53
+ Additionally, it will be the responsibility of the user to assess whether the Generated Code is suitable for the products and services provided by the user. Envio will not bear any responsibility if the Generated Code is found unsuitable for the products and services provided by the user.
54
+
55
+ ### 4. Additional Terms
56
+
57
+ **Disclaimer of Warranties and Limitation of Liability:**
58
+
59
+ (i) Unless expressly undertaken by the Licensor separately, the Licensed Material is provided on an as-is, as-available basis, and the Licensor makes no representations or warranties of any kind regarding the Licensed Material, whether express, implied, statutory, or otherwise. This encompasses, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether known or discoverable. If disclaimers of warranties are not permitted in whole or in part, this disclaimer may not apply to You.
60
+
61
+ (ii) To the fullest extent permitted by law, under no circumstances shall the Licensor be liable to You under any legal theory (including, but not limited to, negligence) for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising from the use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. If limitations of liability are not permitted in whole or in part, this limitation may not apply to You.
62
+
63
+ (iii) The disclaimers of warranties and limitations of liability outlined above shall be construed in a manner that most closely approximates an absolute disclaimer and waiver of all liability, to the fullest extent permitted by law.
64
+
65
+ **Applicable Law and Competent Courts:** This EULA and shall be governed by and construed in accordance with the laws of England. The courts of England shall have exclusive jurisdiction to settle any dispute arising out of or in connection with this EULA.
66
+
67
+ **Additional Agreements:** If the user chooses to use the Software, it may be required to agree to additional terms or agreements outside of this EULA.
@@ -0,0 +1,45 @@
1
+ # Standard SaaS End User License Agreement (EULA)
2
+
3
+ ## End User License Agreement (EULA)
4
+
5
+ This End User License Agreement ("Agreement") is between Envio ("Company") and the user ("User") who downloads, installs, or uses the Software as a Service (SaaS) provided by the Company.
6
+
7
+ ### 1. License Grant
8
+
9
+ 1.1 Company grants User a non-exclusive, non-transferable, limited license to access and use the SaaS for business or personal purposes in accordance with this Agreement.
10
+
11
+ ### 2. Restrictions
12
+
13
+ 2.1 User shall not:
14
+
15
+ - Modify, copy, or create derivative works based on the SaaS.
16
+ - Reverse engineer, decompile, or disassemble the SaaS.
17
+ - Rent, lease, lend, sell, redistribute, or sublicense the SaaS.
18
+ - Use the SaaS for any unlawful purpose.
19
+
20
+ ### 3. Ownership
21
+
22
+ 3.1 The Company retains all rights, title, and interest in and to the SaaS, including all intellectual property rights.
23
+
24
+ ### 4. Term and Termination
25
+
26
+ 4.1 This Agreement is effective until terminated. User’s rights under this Agreement will terminate automatically without notice if User fails to comply with any term(s) of this Agreement.
27
+ 4.2 Upon termination, User must cease all use of the SaaS and destroy all copies of any related documentation.
28
+
29
+ ### 5. Disclaimer of Warranties
30
+
31
+ 5.1 The SaaS is provided "AS IS" without warranty of any kind, either express or implied, including, but not limited to, implied warranties of merchantability and fitness for a particular purpose.
32
+
33
+ ### 6. Limitation of Liability
34
+
35
+ 6.1 In no event shall Company be liable for any special, incidental, indirect, or consequential damages whatsoever arising out of the use of or inability to use the SaaS.
36
+
37
+ ### 7. Governing Law
38
+
39
+ 7.1 This Agreement shall be governed by and construed in accordance with the laws of the United Kingdom.
40
+
41
+ ### 8. Entire Agreement
42
+
43
+ 8.1 This Agreement constitutes the entire agreement between the parties with respect to the use of the SaaS and supersedes all prior or contemporaneous understandings regarding such subject matter.
44
+
45
+ **By using the SaaS, the User agrees to be bound by this Agreement.**
@@ -0,0 +1,35 @@
1
+ # Licensing Information
2
+
3
+ ## Overview
4
+
5
+ Envio's licensing reflects open source ethos but is not OSI recognized. Developers can use Envio's services without vendor lock-in, either by self-hosting or specifying an RPC URL. The generated code is open and public and so is this repository that generates the generated code.
6
+
7
+ Our licenses allows self-hosting but restricts third-party hosting with Envio's hosted service. Envio may consider open-sourcing in the future but prioritizes stakeholder interests and market traction. Please reach out to us on discord if you would like to explore hosting indexers as a third-party.
8
+
9
+ ## TL;DR
10
+
11
+ - **Envio's licensing** mirrors open source benefits but is not OSI recognized.
12
+ - **Developers** can use Envio's services without vendor lock-in by:
13
+ - Self-hosting.
14
+ - Specifying an RPC URL.
15
+ - **Code** is open and public.
16
+ - **Generated license** allows self-hosting but restricts third-party competition.
17
+ - **Code generator license** is a commercial license.
18
+ - **non-comercial use** is allowed.
19
+ - **forking and modifying code** is not allowed. However, if you have specific needs or use cases, please contact us to discuss potential exceptions.
20
+ - **Future considerations** for open-sourcing are dependent on market traction and stakeholder interests.
21
+ - **Contributors** must agree to our Contributor License Agreement (CLA).
22
+
23
+ ## Our Position
24
+
25
+ We're developers who value the open source ethos, which is why our licensing mirrors many benefits of open source licensing. However, Envio and its products do not use a recognized open source license by the OSI. We are public and open, and our licensing reflects this.
26
+
27
+ Our future business model lies in our hosted service and HyperSync requests, and we are protecting this. To ensure continuity and avoid vendor lock-in, developers can run and develop on their indexer without our hosted service. This can be achieved either by self-hosting, which our license permits, or by specifying an RPC URL in their indexer configuration, thus bypassing HyperSync.
28
+
29
+ Envio is in its formative stages. Although we may consider open-sourcing the software in the future, we are committed to ensuring the best interests of all stakeholders. Going open source is somewhat of a one-way function; it's easier to open source than to go "closed source." Once we have gained more market traction, we will review our position on going open source.
30
+
31
+ ## Our Licenses
32
+
33
+ - [EULA](./EULA.md) - The license in your indexer pertaining to the generated code
34
+ - [LICENSE](./LICENSE.md) - The non-commercial use license pertaining to this code base which is used to template & generate the indexing code
35
+ - [CLA](./CLA.md) - The Contributor License Agreement for those who wish to contribute to the project
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "envio",
3
- "version": "3.3.0-alpha.10",
3
+ "version": "3.3.0-alpha.12",
4
4
  "type": "module",
5
5
  "description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
6
6
  "bin": "./bin.mjs",
@@ -11,7 +11,7 @@
11
11
  "url": "git+https://github.com/enviodev/hyperindex.git"
12
12
  },
13
13
  "author": "envio contributors <about@envio.dev>",
14
- "license": "GPL-3.0",
14
+ "license": "SEE LICENSE IN licenses/README.md",
15
15
  "bugs": {
16
16
  "url": "https://github.com/enviodev/hyperindex/issues"
17
17
  },
@@ -37,7 +37,8 @@
37
37
  "rescript.json",
38
38
  "index.d.ts",
39
39
  "index.js",
40
- "src"
40
+ "src",
41
+ "licenses"
41
42
  ],
42
43
  "dependencies": {
43
44
  "@clickhouse/client": "1.17.0",
@@ -69,10 +70,10 @@
69
70
  "tsx": "4.21.0"
70
71
  },
71
72
  "optionalDependencies": {
72
- "envio-linux-x64": "3.3.0-alpha.10",
73
- "envio-linux-x64-musl": "3.3.0-alpha.10",
74
- "envio-linux-arm64": "3.3.0-alpha.10",
75
- "envio-darwin-x64": "3.3.0-alpha.10",
76
- "envio-darwin-arm64": "3.3.0-alpha.10"
73
+ "envio-linux-x64": "3.3.0-alpha.12",
74
+ "envio-linux-x64-musl": "3.3.0-alpha.12",
75
+ "envio-linux-arm64": "3.3.0-alpha.12",
76
+ "envio-darwin-x64": "3.3.0-alpha.12",
77
+ "envio-darwin-arm64": "3.3.0-alpha.12"
77
78
  }
78
79
  }
package/src/Batch.res.mjs CHANGED
@@ -27,7 +27,7 @@ function getProgressedChainsById(chainsBeforeBatch, batchSizePerChain, progressB
27
27
  let progressedChain;
28
28
  if (batchSize !== undefined) {
29
29
  let leftItems = fetchState.buffer.slice(batchSize);
30
- progressedChain = getChainAfterBatchIfProgressed(chainBeforeBatch, progressBlockNumberAfterBatch, FetchState.updateInternal(fetchState, undefined, leftItems, undefined, undefined), batchSize);
30
+ progressedChain = getChainAfterBatchIfProgressed(chainBeforeBatch, progressBlockNumberAfterBatch, FetchState.updateInternal(fetchState, undefined, leftItems, undefined, undefined, undefined), batchSize);
31
31
  } else {
32
32
  progressedChain = getChainAfterBatchIfProgressed(chainBeforeBatch, progressBlockNumberAfterBatch, chainBeforeBatch.fetchState, 0);
33
33
  }
@@ -49,9 +49,9 @@ let runContractRegistersOrThrow = async (
49
49
  for idx in 0 to itemsWithContractRegister->Array.length - 1 {
50
50
  let item = itemsWithContractRegister->Array.getUnsafe(idx)
51
51
  let eventItem = item->Internal.castUnsafeEventItem
52
- let contractRegister = switch eventItem {
53
- | {onEventRegistration: {contractRegister: Some(contractRegister)}} => contractRegister
54
- | {onEventRegistration: {contractRegister: None, eventConfig: {name: eventName}}} =>
52
+ let contractRegister = switch eventItem.onEventRegistration {
53
+ | {contractRegister: Some(contractRegister)} => contractRegister
54
+ | {contractRegister: None, eventConfig: {name: eventName}} =>
55
55
  // Unexpected case, since we should pass only events with contract register to this function
56
56
  JsError.throwWithMessage("Contract register is not set for event " ++ eventName)
57
57
  }
@@ -151,15 +151,26 @@ let rec onQueryResponse = async (
151
151
  let eventItem = item->Internal.castUnsafeEventItem
152
152
  eventItem.onEventRegistration.contractRegister !== None ? count + 1 : count
153
153
  })
154
- Logging.trace({
155
- "msg": "Finished querying",
156
- "chainId": chain->ChainMap.Chain.toChainId,
157
- "partitionId": query.partitionId,
158
- "fromBlock": fromBlockQueried,
159
- "toBlock": latestFetchedBlockNumber,
160
- "numEvents": parsedQueueItems->Array.length,
161
- "numContractRegisterEvents": numContractRegisterEvents,
162
- })
154
+ if numContractRegisterEvents === 0 {
155
+ Logging.trace({
156
+ "msg": "Finished querying",
157
+ "chainId": chain->ChainMap.Chain.toChainId,
158
+ "partitionId": query.partitionId,
159
+ "fromBlock": fromBlockQueried,
160
+ "toBlock": latestFetchedBlockNumber,
161
+ "numEvents": parsedQueueItems->Array.length,
162
+ })
163
+ } else {
164
+ Logging.trace({
165
+ "msg": "Finished querying",
166
+ "chainId": chain->ChainMap.Chain.toChainId,
167
+ "partitionId": query.partitionId,
168
+ "fromBlock": fromBlockQueried,
169
+ "toBlock": latestFetchedBlockNumber,
170
+ "numEvents": parsedQueueItems->Array.length,
171
+ "numContractRegisterEvents": numContractRegisterEvents,
172
+ })
173
+ }
163
174
 
164
175
  let reorgResult = chainState->ChainState.registerReorgGuard(~blockHashes, ~knownHeight)
165
176
 
@@ -212,17 +223,18 @@ let rec onQueryResponse = async (
212
223
  // kick (eg from the processing loop quiescing) collapses into this one.
213
224
  scheduleRollback()
214
225
  | None =>
226
+ // Drop over-fetched events (a merged partition returning an address before
227
+ // its effectiveStartBlock, or a wildcard param referencing an address
228
+ // registered after the log's block) before contract registration, so they
229
+ // neither spawn dynamic contracts nor enter the buffer.
230
+ let newItems = chainState->ChainState.filterByClientAddress(parsedQueueItems)
215
231
  let itemsWithContractRegister = []
216
- let newItems = []
217
- for idx in 0 to parsedQueueItems->Array.length - 1 {
218
- let item = parsedQueueItems->Array.getUnsafe(idx)
232
+ for idx in 0 to newItems->Array.length - 1 {
233
+ let item = newItems->Array.getUnsafe(idx)
219
234
  let eventItem = item->Internal.castUnsafeEventItem
220
235
  if eventItem.onEventRegistration.contractRegister !== None {
221
236
  itemsWithContractRegister->Array.push(item)
222
237
  }
223
- // TODO: Don't really need to keep it in the queue
224
- // when there's no handler (besides raw_events, processed counter, and dcsToStore consuming)
225
- newItems->Array.push(item)
226
238
  }
227
239
 
228
240
  // Re-check staleness: contract registration is async, so the chain state
@@ -111,15 +111,26 @@ async function onQueryResponse(state, param, stateId, scheduleFetch, schedulePro
111
111
  return count;
112
112
  }
113
113
  });
114
- Logging.trace({
115
- msg: "Finished querying",
116
- chainId: chain,
117
- partitionId: query.partitionId,
118
- fromBlock: fromBlockQueried,
119
- toBlock: latestFetchedBlockNumber,
120
- numEvents: parsedQueueItems.length,
121
- numContractRegisterEvents: numContractRegisterEvents
122
- });
114
+ if (numContractRegisterEvents === 0) {
115
+ Logging.trace({
116
+ msg: "Finished querying",
117
+ chainId: chain,
118
+ partitionId: query.partitionId,
119
+ fromBlock: fromBlockQueried,
120
+ toBlock: latestFetchedBlockNumber,
121
+ numEvents: parsedQueueItems.length
122
+ });
123
+ } else {
124
+ Logging.trace({
125
+ msg: "Finished querying",
126
+ chainId: chain,
127
+ partitionId: query.partitionId,
128
+ fromBlock: fromBlockQueried,
129
+ toBlock: latestFetchedBlockNumber,
130
+ numEvents: parsedQueueItems.length,
131
+ numContractRegisterEvents: numContractRegisterEvents
132
+ });
133
+ }
123
134
  let reorgResult = ChainState.registerReorgGuard(chainState, response.blockHashes, knownHeight);
124
135
  let rollbackWithReorgDetectedBlockNumber;
125
136
  if (typeof reorgResult !== "object") {
@@ -139,14 +150,13 @@ async function onQueryResponse(state, param, stateId, scheduleFetch, schedulePro
139
150
  IndexerState.beginReorg(state, chain, rollbackWithReorgDetectedBlockNumber);
140
151
  return scheduleRollback();
141
152
  }
153
+ let newItems = ChainState.filterByClientAddress(chainState, parsedQueueItems);
142
154
  let itemsWithContractRegister = [];
143
- let newItems = [];
144
- for (let idx = 0, idx_finish = parsedQueueItems.length; idx < idx_finish; ++idx) {
145
- let item = parsedQueueItems[idx];
155
+ for (let idx = 0, idx_finish = newItems.length; idx < idx_finish; ++idx) {
156
+ let item = newItems[idx];
146
157
  if (item.onEventRegistration.contractRegister !== undefined) {
147
158
  itemsWithContractRegister.push(item);
148
159
  }
149
- newItems.push(item);
150
160
  }
151
161
  let proceed = newItemsWithDcs => {
152
162
  if (!IndexerState.isStale(state, stateId)) {
@@ -4,6 +4,8 @@
4
4
 
5
5
  type t = {
6
6
  logger: Pino.t,
7
+ // The registrations used to build this chain's sources and route native items.
8
+ onEventRegistrations: array<Internal.onEventRegistration>,
7
9
  mutable fetchState: FetchState.t,
8
10
  // The chain-wide address index. Not `mutable`: the dict is mutated in place by
9
11
  // register/rollback, so the reference is stable across fetchState versions.
@@ -71,9 +73,22 @@ let configAddresses = (chainConfig: Config.chain): array<Internal.indexingAddres
71
73
  addresses
72
74
  }
73
75
 
76
+ let validateOnEventRegistrations = (
77
+ ~chainId: int,
78
+ registrations: array<Internal.onEventRegistration>,
79
+ ) =>
80
+ registrations->Array.forEachWithIndex((registration, expectedIndex) => {
81
+ if registration.index !== expectedIndex {
82
+ JsError.throwWithMessage(
83
+ `Invalid onEvent registration index for chain ${chainId->Int.toString}: ${registration.eventConfig.contractName}.${registration.eventConfig.name} has index ${registration.index->Int.toString}, but its ChainState position is ${expectedIndex->Int.toString}.`,
84
+ )
85
+ }
86
+ })
87
+
74
88
  let make = (
75
89
  ~chainConfig: Config.chain,
76
90
  ~fetchState: FetchState.t,
91
+ ~onEventRegistrations=[],
77
92
  ~indexingAddresses: IndexingAddresses.t,
78
93
  ~sourceManager: SourceManager.t,
79
94
  ~reorgDetection: ReorgDetection.t,
@@ -87,22 +102,26 @@ let make = (
87
102
  ~blockStore=BlockStore.make(~ecosystem=Ecosystem.Evm, ~shouldChecksum=false),
88
103
  ~logger: Pino.t,
89
104
  ): t => {
90
- logger,
91
- fetchState,
92
- indexingAddresses,
93
- sourceManager,
94
- chainConfig,
95
- isProgressAtHead,
96
- timestampCaughtUpToHeadOrEndblock,
97
- committedProgressBlockNumber,
98
- processingBlockNumber: committedProgressBlockNumber,
99
- numEventsProcessed,
100
- pendingBudget: 0.,
101
- chainDensity,
102
- reorgDetection,
103
- safeCheckpointTracking,
104
- transactionStore,
105
- blockStore,
105
+ validateOnEventRegistrations(~chainId=chainConfig.id, onEventRegistrations)
106
+ {
107
+ logger,
108
+ onEventRegistrations,
109
+ fetchState,
110
+ indexingAddresses,
111
+ sourceManager,
112
+ chainConfig,
113
+ isProgressAtHead,
114
+ timestampCaughtUpToHeadOrEndblock,
115
+ committedProgressBlockNumber,
116
+ processingBlockNumber: committedProgressBlockNumber,
117
+ numEventsProcessed,
118
+ pendingBudget: 0.,
119
+ chainDensity,
120
+ reorgDetection,
121
+ safeCheckpointTracking,
122
+ transactionStore,
123
+ blockStore,
124
+ }
106
125
  }
107
126
 
108
127
  let makeInternal = (
@@ -238,6 +257,7 @@ let makeInternal = (
238
257
  make(
239
258
  ~chainConfig,
240
259
  ~fetchState,
260
+ ~onEventRegistrations,
241
261
  ~indexingAddresses=indexingAddressIndex,
242
262
  ~sourceManager=SourceManager.make(~sources, ~isRealtime, ~reducedPollingInterval?),
243
263
  ~reorgDetection=ReorgDetection.make(
@@ -372,7 +392,8 @@ let isReadyToEnterReorgThreshold = (cs: t) => cs.fetchState->FetchState.isReadyT
372
392
  let startFetchingQueries = (cs: t, ~queries: array<FetchState.query>) => {
373
393
  cs.fetchState->FetchState.startFetchingQueries(~queries)
374
394
  cs.pendingBudget =
375
- cs.pendingBudget +. queries->Array.reduce(0., (acc, query) => acc +. query.itemsEst->Int.toFloat)
395
+ cs.pendingBudget +.
396
+ queries->Array.reduce(0., (acc, query) => acc +. query.itemsEst->Int.toFloat)
376
397
  }
377
398
 
378
399
  // Drop every in-flight query and release their reservations together, keeping
@@ -445,16 +466,12 @@ let targetBlock = (cs: t, ~chainTargetItems: float) => {
445
466
  }
446
467
 
447
468
  // Block range that cross-chain progress alignment maps fractions over: from
448
- // the first block that can hold this chain's events to the last block it will
449
- // fetch.
469
+ // the first block that can hold this chain's events to the last block it can
470
+ // fetch right now.
450
471
  let progressRange = (cs: t) => {
451
472
  let fetchState = cs.fetchState
452
473
  let lower = fetchState.firstEventBlock->Option.getOr(fetchState.startBlock)
453
- let upper = switch fetchState.endBlock {
454
- | Some(endBlock) => endBlock
455
- | None => fetchState.knownHeight
456
- }
457
- (lower, upper)
474
+ (lower, cs->fetchCeiling)
458
475
  }
459
476
 
460
477
  // A degenerate range (chain already at or past its last block) maps to 1 so it
@@ -749,6 +766,9 @@ let materializePageItems = async (
749
766
  ))
750
767
  }
751
768
 
769
+ let filterByClientAddress = (cs: t, items: array<Internal.item>): array<Internal.item> =>
770
+ items->FetchState.filterByClientAddress(~indexingAddresses=cs.indexingAddresses)
771
+
752
772
  let handleQueryResult = (
753
773
  cs: t,
754
774
  ~query: FetchState.query,
@@ -781,12 +801,7 @@ let handleQueryResult = (
781
801
 
782
802
  cs.fetchState =
783
803
  fs
784
- ->FetchState.handleQueryResult(
785
- ~indexingAddresses=cs.indexingAddresses,
786
- ~query,
787
- ~latestFetchedBlock,
788
- ~newItems,
789
- )
804
+ ->FetchState.handleQueryResult(~query, ~latestFetchedBlock, ~newItems)
790
805
  ->FetchState.updateKnownHeight(~knownHeight)
791
806
 
792
807
  // The query is no longer in flight, so release its reservation.
@@ -40,7 +40,16 @@ function configAddresses(chainConfig) {
40
40
  return addresses;
41
41
  }
42
42
 
43
- function make(chainConfig, fetchState, indexingAddresses, sourceManager, reorgDetection, committedProgressBlockNumber, safeCheckpointTrackingOpt, numEventsProcessedOpt, timestampCaughtUpToHeadOrEndblockOpt, isProgressAtHeadOpt, transactionStoreOpt, chainDensityOpt, blockStoreOpt, logger) {
43
+ function validateOnEventRegistrations(chainId, registrations) {
44
+ registrations.forEach((registration, expectedIndex) => {
45
+ if (registration.index !== expectedIndex) {
46
+ return Stdlib_JsError.throwWithMessage(`Invalid onEvent registration index for chain ` + chainId.toString() + `: ` + registration.eventConfig.contractName + `.` + registration.eventConfig.name + ` has index ` + registration.index.toString() + `, but its ChainState position is ` + expectedIndex.toString() + `.`);
47
+ }
48
+ });
49
+ }
50
+
51
+ function make(chainConfig, fetchState, onEventRegistrationsOpt, indexingAddresses, sourceManager, reorgDetection, committedProgressBlockNumber, safeCheckpointTrackingOpt, numEventsProcessedOpt, timestampCaughtUpToHeadOrEndblockOpt, isProgressAtHeadOpt, transactionStoreOpt, chainDensityOpt, blockStoreOpt, logger) {
52
+ let onEventRegistrations = onEventRegistrationsOpt !== undefined ? onEventRegistrationsOpt : [];
44
53
  let safeCheckpointTracking = safeCheckpointTrackingOpt !== undefined ? Primitive_option.valFromOption(safeCheckpointTrackingOpt) : undefined;
45
54
  let numEventsProcessed = numEventsProcessedOpt !== undefined ? numEventsProcessedOpt : 0;
46
55
  let timestampCaughtUpToHeadOrEndblock = timestampCaughtUpToHeadOrEndblockOpt !== undefined ? Primitive_option.valFromOption(timestampCaughtUpToHeadOrEndblockOpt) : undefined;
@@ -48,8 +57,10 @@ function make(chainConfig, fetchState, indexingAddresses, sourceManager, reorgDe
48
57
  let transactionStore = transactionStoreOpt !== undefined ? Primitive_option.valFromOption(transactionStoreOpt) : TransactionStore.make("evm", false);
49
58
  let chainDensity = chainDensityOpt !== undefined ? Primitive_option.valFromOption(chainDensityOpt) : undefined;
50
59
  let blockStore = blockStoreOpt !== undefined ? Primitive_option.valFromOption(blockStoreOpt) : BlockStore.make("evm", false);
60
+ validateOnEventRegistrations(chainConfig.id, onEventRegistrations);
51
61
  return {
52
62
  logger: logger,
63
+ onEventRegistrations: onEventRegistrations,
53
64
  fetchState: fetchState,
54
65
  indexingAddresses: indexingAddresses,
55
66
  sourceManager: sourceManager,
@@ -136,7 +147,7 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
136
147
  }
137
148
  let firstEventBlock$1 = fetchState.firstEventBlock;
138
149
  let chainDensity = firstEventBlock$1 !== undefined && progressBlockNumber > firstEventBlock$1 && numEventsProcessed > 0 ? numEventsProcessed / (progressBlockNumber - firstEventBlock$1 | 0) : undefined;
139
- return make(chainConfig, fetchState, indexingAddressIndex, SourceManager.make(sources$1, isRealtime, undefined, undefined, undefined, reducedPollingInterval, undefined, undefined), ReorgDetection.make(chainReorgCheckpoints, maxReorgDepth, config.shouldRollbackOnReorg), progressBlockNumber, Primitive_option.some(SafeCheckpointTracking.make(maxReorgDepth, config.shouldRollbackOnReorg, chainReorgCheckpoints)), numEventsProcessed, Primitive_option.some(timestampCaughtUpToHeadOrEndblock), undefined, Primitive_option.some(TransactionStore.make(config.ecosystem.name, !lowercaseAddresses)), Primitive_option.some(chainDensity), Primitive_option.some(BlockStore.make(config.ecosystem.name, !lowercaseAddresses)), logger);
150
+ return make(chainConfig, fetchState, onEventRegistrations, indexingAddressIndex, SourceManager.make(sources$1, isRealtime, undefined, undefined, undefined, reducedPollingInterval, undefined, undefined), ReorgDetection.make(chainReorgCheckpoints, maxReorgDepth, config.shouldRollbackOnReorg), progressBlockNumber, Primitive_option.some(SafeCheckpointTracking.make(maxReorgDepth, config.shouldRollbackOnReorg, chainReorgCheckpoints)), numEventsProcessed, Primitive_option.some(timestampCaughtUpToHeadOrEndblock), undefined, Primitive_option.some(TransactionStore.make(config.ecosystem.name, !lowercaseAddresses)), Primitive_option.some(chainDensity), Primitive_option.some(BlockStore.make(config.ecosystem.name, !lowercaseAddresses)), logger);
140
151
  }
141
152
 
142
153
  function makeFromConfig(chainConfig, config, registrationsByChainId, knownHeight) {
@@ -290,11 +301,9 @@ function targetBlock(cs, chainTargetItems) {
290
301
  function progressRange(cs) {
291
302
  let fetchState = cs.fetchState;
292
303
  let lower = Stdlib_Option.getOr(fetchState.firstEventBlock, fetchState.startBlock);
293
- let endBlock = fetchState.endBlock;
294
- let upper = endBlock !== undefined ? endBlock : fetchState.knownHeight;
295
304
  return [
296
305
  lower,
297
- upper
306
+ fetchCeiling(cs)
298
307
  ];
299
308
  }
300
309
 
@@ -498,6 +507,10 @@ async function materializePageItems(items, transactionStore, blockStore, ecosyst
498
507
  ]);
499
508
  }
500
509
 
510
+ function filterByClientAddress(cs, items) {
511
+ return FetchState.filterByClientAddress(items, cs.indexingAddresses);
512
+ }
513
+
501
514
  function handleQueryResult(cs, query, newItems, newItemsWithDcs, latestFetchedBlock, knownHeight, txPage, blockPage) {
502
515
  if (txPage !== undefined) {
503
516
  cs.transactionStore.merge(Primitive_option.valFromOption(txPage));
@@ -506,7 +519,7 @@ function handleQueryResult(cs, query, newItems, newItemsWithDcs, latestFetchedBl
506
519
  cs.blockStore.merge(Primitive_option.valFromOption(blockPage));
507
520
  }
508
521
  let fs = newItemsWithDcs.length !== 0 ? FetchState.registerDynamicContracts(cs.fetchState, cs.indexingAddresses, newItemsWithDcs) : cs.fetchState;
509
- cs.fetchState = FetchState.updateKnownHeight(FetchState.handleQueryResult(fs, cs.indexingAddresses, query, latestFetchedBlock, newItems), knownHeight);
522
+ cs.fetchState = FetchState.updateKnownHeight(FetchState.handleQueryResult(fs, query, latestFetchedBlock, newItems), knownHeight);
510
523
  cs.pendingBudget = Primitive_float.max(0, cs.pendingBudget - query.itemsEst);
511
524
  }
512
525
 
@@ -571,7 +584,7 @@ function setEndBlockToFirstEvent(cs, blockNumber) {
571
584
  }
572
585
 
573
586
  function enterReorgThreshold(cs) {
574
- cs.fetchState = FetchState.updateInternal(cs.fetchState, undefined, undefined, cs.chainConfig.blockLag, undefined);
587
+ cs.fetchState = FetchState.updateInternal(cs.fetchState, undefined, undefined, undefined, cs.chainConfig.blockLag, undefined);
575
588
  }
576
589
 
577
590
  function toChainMetadata(cs) {
@@ -619,7 +632,7 @@ function isReadyToEnterReorgThresholdAfterBatch(cs, batch) {
619
632
  function advanceAfterBatch(cs, batch, enteringReorgThreshold) {
620
633
  let chainAfterBatch = batch.progressedChainsById[cs.fetchState.chainId];
621
634
  if (chainAfterBatch !== undefined) {
622
- cs.fetchState = enteringReorgThreshold ? FetchState.updateInternal(chainAfterBatch.fetchState, undefined, undefined, cs.chainConfig.blockLag, undefined) : chainAfterBatch.fetchState;
635
+ cs.fetchState = enteringReorgThreshold ? FetchState.updateInternal(chainAfterBatch.fetchState, undefined, undefined, undefined, cs.chainConfig.blockLag, undefined) : chainAfterBatch.fetchState;
623
636
  cs.processingBlockNumber = chainAfterBatch.progressBlockNumber;
624
637
  return;
625
638
  }
@@ -783,6 +796,7 @@ export {
783
796
  isReady,
784
797
  isFetchingAtHead,
785
798
  isAtHeadWithoutEndBlock,
799
+ filterByClientAddress,
786
800
  handleQueryResult,
787
801
  materializeBatchItems,
788
802
  materializePageItems,
@@ -8,6 +8,7 @@ let configAddresses: Config.chain => array<Internal.indexingAddress>
8
8
  let make: (
9
9
  ~chainConfig: Config.chain,
10
10
  ~fetchState: FetchState.t,
11
+ ~onEventRegistrations: array<Internal.onEventRegistration>=?,
11
12
  ~indexingAddresses: IndexingAddresses.t,
12
13
  ~sourceManager: SourceManager.t,
13
14
  ~reorgDetection: ReorgDetection.t,
@@ -112,6 +113,10 @@ let isReady: t => bool
112
113
  let isFetchingAtHead: t => bool
113
114
  let isAtHeadWithoutEndBlock: t => bool
114
115
 
116
+ // Drop over-fetched events an address-param filter rejects, before contract
117
+ // registration runs on them. Uses the chain's current indexing addresses.
118
+ let filterByClientAddress: (t, array<Internal.item>) => array<Internal.item>
119
+
115
120
  // State transitions. The chain state is mutated only through these.
116
121
  let handleQueryResult: (
117
122
  t,
package/src/Config.res CHANGED
@@ -200,14 +200,18 @@ let svmEventDescriptorSchema = S.schema(s =>
200
200
  "transactionFields": s.matches(S.array(Internal.svmTransactionFieldSchema)),
201
201
  "blockFields": s.matches(S.option(S.array(Internal.svmBlockFieldSchema))),
202
202
  "includeLogs": s.matches(S.bool),
203
+ // An array of AND-groups OR-ed together: the CLI normalizes both the flat
204
+ // and `any_of` YAML shapes to `Vec<Vec<SvmAccountFilterJson>>`.
203
205
  "accountFilters": s.matches(
204
206
  S.option(
205
207
  S.array(
206
- S.schema(s =>
207
- {
208
- "position": s.matches(S.int),
209
- "values": s.matches(S.array(S.string)),
210
- }
208
+ S.array(
209
+ S.schema(s =>
210
+ {
211
+ "position": s.matches(S.int),
212
+ "values": s.matches(S.array(S.string)),
213
+ }
214
+ ),
211
215
  ),
212
216
  ),
213
217
  ),