envio 2.14.2 → 2.15.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.
- package/README.md +31 -19
- package/package.json +5 -5
- package/src/Internal.gen.ts +4 -0
- package/src/Internal.res +44 -18
- package/src/LogSelection.res +56 -12
- package/src/Utils.res +0 -2
- package/src/bindings/OpaqueTypes.ts +1 -0
- package/src/sources/HyperSyncClient.res +2 -4
- package/src/sources/HyperSyncJsonApi.res +6 -2
- package/src/sources/Rpc.res +1 -1
package/README.md
CHANGED
|
@@ -1,33 +1,45 @@
|
|
|
1
|
-
#
|
|
1
|
+
# HyperIndex: Ultra-Fast Multichain Indexer
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/enviodev/hyperindex/releases) [](https://github.com/enviodev/hyperindex/issues) [](https://github.com/enviodev/hyperindex/graphs/contributors) [](https://discord.gg/DhfFhzuJQh) [](https://github.com/enviodev/hyperindex/stargazers)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
HyperIndex is a fast, developer-friendly multichain indexer, optimized for both local development and reliable hosted deployment. It empowers developers to effortlessly build robust backends for blockchain applications.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
> For comprehensive documentation, visit our [Documentation Portal](https://docs.envio.dev).
|
|
8
|
+
> For hosted services and other solutions, visit [envio.dev](https://envio.dev).
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+

|
|
10
11
|
|
|
11
12
|
## Key Features
|
|
12
13
|
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
14
|
+
- **[Indexer auto-generation](https://docs.envio.dev/docs/HyperIndex/contract-import)** – Generate Indexers directly from smart contract addresses
|
|
15
|
+
- **High performance** – Historical backfills at over 5,000+ events per second ([fastest in market](https://docs.envio.dev/blog/indexer-benchmarking-results))
|
|
16
|
+
- **Local development** – Full-featured local environment with Docker
|
|
17
|
+
- **[Multichain indexing](https://docs.envio.dev/docs/HyperIndex/multichain-indexing)** – Index any EVM-compatible blockchain and Fuel (simultaneously)
|
|
18
|
+
- **Real-time indexing** – Instantly track blockchain events
|
|
19
|
+
- **[Reorg support](https://docs.envio.dev/docs/HyperIndex/reorgs-support)** – Graceful handling of blockchain reorganizations
|
|
20
|
+
- **GraphQL API** – Easy-to-query indexed data
|
|
21
|
+
- **Flexible language support** – JavaScript, TypeScript, and ReScript
|
|
22
|
+
- **Factory contract support** – Index data from 100,000+ factory contracts seamlessly
|
|
23
|
+
- **On-chain & off-chain data integration** – Easily combine multiple data sources
|
|
24
|
+
- **[Self-hosted & managed options](https://docs.envio.dev/docs/HyperIndex/hosted-service)** – Run your own setup or use HyperIndex hosted services
|
|
25
|
+
- **Detailed logging & error reporting** – Debug and optimize with clarity
|
|
26
|
+
- **External API actions** – Trigger external services based on blockchain events
|
|
27
|
+
- **[Wildcard topic indexing](https://docs.envio.dev/docs/HyperIndex/wildcard-indexing)** – Flexible indexing based on event topics
|
|
28
|
+
- **Fallback RPC data sources** – Enhanced reliability with RPC connections
|
|
22
29
|
|
|
23
30
|
## Getting Started
|
|
24
31
|
|
|
25
|
-
Check out our [Getting Started](https://docs.envio.dev/docs/HyperIndex/getting-started)
|
|
32
|
+
Check out our [Getting Started Guide](https://docs.envio.dev/docs/HyperIndex/getting-started) to start querying your smart contract data with just a few clicks!
|
|
26
33
|
|
|
27
|
-
##
|
|
34
|
+
## Performance
|
|
35
|
+
|
|
36
|
+
HyperIndex delivers industry-leading indexing speeds, with capability to process thousands of events per second while maintaining data integrity across multiple chains.
|
|
28
37
|
|
|
29
|
-
|
|
38
|
+
## Contribution & Support
|
|
30
39
|
|
|
31
|
-
|
|
40
|
+
- Follow us on [X](https://twitter.com/envio_indexer)
|
|
41
|
+
- Join our [Discord community](https://discord.gg/DhfFhzuJQh)
|
|
42
|
+
- Report issues on [GitHub](https://github.com/enviodev/hyperindex/issues/new/choose)
|
|
43
|
+
- Check our [Common Issues](https://docs.envio.dev/docs/common-issues) page for troubleshooting
|
|
32
44
|
|
|
33
|
-
|
|
45
|
+
If you find HyperIndex useful, please consider giving us a ⭐ on this repo!
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "envio",
|
|
3
|
-
"version": "v2.
|
|
3
|
+
"version": "v2.15.0",
|
|
4
4
|
"description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
|
|
5
5
|
"bin": "./bin.js",
|
|
6
6
|
"repository": {
|
|
@@ -23,10 +23,10 @@
|
|
|
23
23
|
},
|
|
24
24
|
"homepage": "https://envio.dev",
|
|
25
25
|
"optionalDependencies": {
|
|
26
|
-
"envio-linux-x64": "v2.
|
|
27
|
-
"envio-linux-arm64": "v2.
|
|
28
|
-
"envio-darwin-x64": "v2.
|
|
29
|
-
"envio-darwin-arm64": "v2.
|
|
26
|
+
"envio-linux-x64": "v2.15.0",
|
|
27
|
+
"envio-linux-arm64": "v2.15.0",
|
|
28
|
+
"envio-darwin-x64": "v2.15.0",
|
|
29
|
+
"envio-darwin-arm64": "v2.15.0"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@envio-dev/hypersync-client": "0.6.3",
|
package/src/Internal.gen.ts
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
/* eslint-disable */
|
|
4
4
|
/* tslint:disable */
|
|
5
5
|
|
|
6
|
+
import type {invalid as $$noEventFilters} from './bindings/OpaqueTypes.ts';
|
|
7
|
+
|
|
6
8
|
import type {t as Address_t} from './Address.gen';
|
|
7
9
|
|
|
8
10
|
export type genericEvent<params,block,transaction> = {
|
|
@@ -45,3 +47,5 @@ export type fuelTransferParams = {
|
|
|
45
47
|
readonly assetId: string;
|
|
46
48
|
readonly amount: bigint
|
|
47
49
|
};
|
|
50
|
+
|
|
51
|
+
export type noEventFilters = $$noEventFilters;
|
package/src/Internal.res
CHANGED
|
@@ -64,22 +64,16 @@ type genericHandlerWithLoader<'loader, 'handler, 'eventFilters> = {
|
|
|
64
64
|
preRegisterDynamicContracts?: bool,
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
type
|
|
68
|
-
|
|
67
|
+
type baseEventConfig = {
|
|
68
|
+
id: string,
|
|
69
|
+
name: string,
|
|
69
70
|
contractName: string,
|
|
71
|
+
isWildcard: bool,
|
|
72
|
+
preRegisterDynamicContracts: bool,
|
|
70
73
|
loader: option<loader>,
|
|
71
74
|
handler: option<handler>,
|
|
72
75
|
contractRegister: option<contractRegister>,
|
|
73
|
-
timestamp: int,
|
|
74
|
-
chain: ChainMap.Chain.t,
|
|
75
|
-
blockNumber: int,
|
|
76
|
-
logIndex: int,
|
|
77
|
-
event: event,
|
|
78
76
|
paramsRawEventSchema: S.schema<eventParams>,
|
|
79
|
-
//Default to false, if an event needs to
|
|
80
|
-
//be reprocessed after it has loaded dynamic contracts
|
|
81
|
-
//This gets set to true and does not try and reload events
|
|
82
|
-
hasRegisteredDynamicContracts?: bool,
|
|
83
77
|
}
|
|
84
78
|
|
|
85
79
|
type fuelEventKind =
|
|
@@ -89,20 +83,47 @@ type fuelEventKind =
|
|
|
89
83
|
| Transfer
|
|
90
84
|
| Call
|
|
91
85
|
type fuelEventConfig = {
|
|
92
|
-
|
|
93
|
-
contractName: string,
|
|
86
|
+
...baseEventConfig,
|
|
94
87
|
kind: fuelEventKind,
|
|
95
|
-
isWildcard: bool,
|
|
96
|
-
loader: option<loader>,
|
|
97
|
-
handler: option<handler>,
|
|
98
|
-
contractRegister: option<contractRegister>,
|
|
99
|
-
paramsRawEventSchema: S.schema<eventParams>,
|
|
100
88
|
}
|
|
101
89
|
type fuelContractConfig = {
|
|
102
90
|
name: string,
|
|
103
91
|
events: array<fuelEventConfig>,
|
|
104
92
|
}
|
|
105
93
|
|
|
94
|
+
type topicSelection = {
|
|
95
|
+
topic0: array<EvmTypes.Hex.t>,
|
|
96
|
+
topic1: array<EvmTypes.Hex.t>,
|
|
97
|
+
topic2: array<EvmTypes.Hex.t>,
|
|
98
|
+
topic3: array<EvmTypes.Hex.t>,
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
type evmEventConfig = {
|
|
102
|
+
...baseEventConfig,
|
|
103
|
+
getTopicSelectionsOrThrow: (~chain: ChainMap.Chain.t) => array<topicSelection>,
|
|
104
|
+
blockSchema: S.schema<eventBlock>,
|
|
105
|
+
transactionSchema: S.schema<eventTransaction>,
|
|
106
|
+
convertHyperSyncEventArgs: HyperSyncClient.Decoder.decodedEvent => eventParams,
|
|
107
|
+
}
|
|
108
|
+
type evmContractConfig = {
|
|
109
|
+
name: string,
|
|
110
|
+
abi: Ethers.abi,
|
|
111
|
+
events: array<evmEventConfig>,
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
type eventItem = {
|
|
115
|
+
eventConfig: baseEventConfig,
|
|
116
|
+
timestamp: int,
|
|
117
|
+
chain: ChainMap.Chain.t,
|
|
118
|
+
blockNumber: int,
|
|
119
|
+
logIndex: int,
|
|
120
|
+
event: event,
|
|
121
|
+
//Default to false, if an event needs to
|
|
122
|
+
//be reprocessed after it has loaded dynamic contracts
|
|
123
|
+
//This gets set to true and does not try and reload events
|
|
124
|
+
hasRegisteredDynamicContracts?: bool,
|
|
125
|
+
}
|
|
126
|
+
|
|
106
127
|
@genType
|
|
107
128
|
type fuelSupplyParams = {
|
|
108
129
|
subId: string,
|
|
@@ -125,3 +146,8 @@ let fuelTransferParamsSchema = S.schema(s => {
|
|
|
125
146
|
})
|
|
126
147
|
|
|
127
148
|
type entity = private {id: string}
|
|
149
|
+
|
|
150
|
+
@genType.import(("./bindings/OpaqueTypes.ts", "invalid"))
|
|
151
|
+
type noEventFilters
|
|
152
|
+
type eventFilters
|
|
153
|
+
type eventFiltersArgs = {chainId: int}
|
package/src/LogSelection.res
CHANGED
|
@@ -1,24 +1,17 @@
|
|
|
1
|
-
type topicSelection = {
|
|
2
|
-
topic0: array<EvmTypes.Hex.t>,
|
|
3
|
-
topic1: array<EvmTypes.Hex.t>,
|
|
4
|
-
topic2: array<EvmTypes.Hex.t>,
|
|
5
|
-
topic3: array<EvmTypes.Hex.t>,
|
|
6
|
-
}
|
|
7
|
-
|
|
8
1
|
exception MissingRequiredTopic0
|
|
9
2
|
let makeTopicSelection = (~topic0, ~topic1=[], ~topic2=[], ~topic3=[]) =>
|
|
10
3
|
if topic0->Utils.Array.isEmpty {
|
|
11
4
|
Error(MissingRequiredTopic0)
|
|
12
5
|
} else {
|
|
13
6
|
{
|
|
14
|
-
topic0,
|
|
7
|
+
Internal.topic0,
|
|
15
8
|
topic1,
|
|
16
9
|
topic2,
|
|
17
10
|
topic3,
|
|
18
11
|
}->Ok
|
|
19
12
|
}
|
|
20
13
|
|
|
21
|
-
let hasFilters = ({topic1, topic2, topic3}: topicSelection) => {
|
|
14
|
+
let hasFilters = ({topic1, topic2, topic3}: Internal.topicSelection) => {
|
|
22
15
|
[topic1, topic2, topic3]->Js.Array2.find(topic => !Utils.Array.isEmpty(topic))->Belt.Option.isSome
|
|
23
16
|
}
|
|
24
17
|
|
|
@@ -26,7 +19,7 @@ let hasFilters = ({topic1, topic2, topic3}: topicSelection) => {
|
|
|
26
19
|
For a group of topic selections, if multiple only use topic0, then they can be compressed into one
|
|
27
20
|
selection combining the topic0s
|
|
28
21
|
*/
|
|
29
|
-
let compressTopicSelections = (topicSelections: array<topicSelection>) => {
|
|
22
|
+
let compressTopicSelections = (topicSelections: array<Internal.topicSelection>) => {
|
|
30
23
|
let topic0sOfSelectionsWithoutFilters = []
|
|
31
24
|
|
|
32
25
|
let selectionsWithFilters = []
|
|
@@ -45,7 +38,7 @@ let compressTopicSelections = (topicSelections: array<topicSelection>) => {
|
|
|
45
38
|
| [] => selectionsWithFilters
|
|
46
39
|
| topic0 =>
|
|
47
40
|
let selectionWithoutFilters = {
|
|
48
|
-
topic0,
|
|
41
|
+
Internal.topic0,
|
|
49
42
|
topic1: [],
|
|
50
43
|
topic2: [],
|
|
51
44
|
topic3: [],
|
|
@@ -56,10 +49,61 @@ let compressTopicSelections = (topicSelections: array<topicSelection>) => {
|
|
|
56
49
|
|
|
57
50
|
type t = {
|
|
58
51
|
addresses: array<Address.t>,
|
|
59
|
-
topicSelections: array<topicSelection>,
|
|
52
|
+
topicSelections: array<Internal.topicSelection>,
|
|
60
53
|
}
|
|
61
54
|
|
|
62
55
|
let make = (~addresses, ~topicSelections) => {
|
|
63
56
|
let topicSelections = compressTopicSelections(topicSelections)
|
|
64
57
|
{addresses, topicSelections}
|
|
65
58
|
}
|
|
59
|
+
|
|
60
|
+
let fromEventFiltersOrThrow = {
|
|
61
|
+
let emptyTopics = []
|
|
62
|
+
let noopGetter = _ => emptyTopics
|
|
63
|
+
|
|
64
|
+
(
|
|
65
|
+
~chain,
|
|
66
|
+
~eventFilters: option<Js.Json.t>,
|
|
67
|
+
~sighash,
|
|
68
|
+
~topic1=noopGetter,
|
|
69
|
+
~topic2=noopGetter,
|
|
70
|
+
~topic3=noopGetter,
|
|
71
|
+
) => {
|
|
72
|
+
let topic0 = [sighash->EvmTypes.Hex.fromStringUnsafe]
|
|
73
|
+
switch eventFilters {
|
|
74
|
+
| None => [
|
|
75
|
+
{
|
|
76
|
+
Internal.topic0,
|
|
77
|
+
topic1: emptyTopics,
|
|
78
|
+
topic2: emptyTopics,
|
|
79
|
+
topic3: emptyTopics,
|
|
80
|
+
},
|
|
81
|
+
]
|
|
82
|
+
| Some(eventFilters) => {
|
|
83
|
+
let eventFilters = if Js.typeof(eventFilters) === "function" {
|
|
84
|
+
(eventFilters->(Utils.magic: Js.Json.t => Internal.eventFiltersArgs => Js.Json.t))({
|
|
85
|
+
chainId: chain->ChainMap.Chain.toChainId,
|
|
86
|
+
})
|
|
87
|
+
} else {
|
|
88
|
+
eventFilters
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
switch eventFilters {
|
|
92
|
+
| Array([]) => [%raw(`{}`)]
|
|
93
|
+
| Array(a) => a
|
|
94
|
+
| _ => [eventFilters]
|
|
95
|
+
}->Js.Array2.map(eventFilter => {
|
|
96
|
+
switch eventFilter {
|
|
97
|
+
| Object(eventFilter) => {
|
|
98
|
+
Internal.topic0,
|
|
99
|
+
topic1: topic1(eventFilter),
|
|
100
|
+
topic2: topic2(eventFilter),
|
|
101
|
+
topic3: topic3(eventFilter),
|
|
102
|
+
}
|
|
103
|
+
| _ => Js.Exn.raiseError("Invalid event filters configuration. Expected an object")
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
package/src/Utils.res
CHANGED
|
@@ -422,13 +422,11 @@ type t = {
|
|
|
422
422
|
|
|
423
423
|
@module("@envio-dev/hypersync-client") @scope("HypersyncClient") external new: cfg => t = "new"
|
|
424
424
|
|
|
425
|
-
let
|
|
426
|
-
|
|
427
|
-
let make = (~url, ~bearerToken: option<string>, ~httpReqTimeoutMillis, ~maxNumRetries) =>
|
|
425
|
+
let make = (~url, ~apiToken, ~httpReqTimeoutMillis, ~maxNumRetries) =>
|
|
428
426
|
new({
|
|
429
427
|
url,
|
|
430
428
|
enableChecksumAddresses: true,
|
|
431
|
-
bearerToken:
|
|
429
|
+
bearerToken: apiToken,
|
|
432
430
|
httpReqTimeoutMillis,
|
|
433
431
|
maxNumRetries,
|
|
434
432
|
})
|
|
@@ -360,13 +360,17 @@ module ResponseTypes = {
|
|
|
360
360
|
let queryRoute = Rest.route(() => {
|
|
361
361
|
path: "/query",
|
|
362
362
|
method: Post,
|
|
363
|
-
input: s =>
|
|
363
|
+
input: s =>
|
|
364
|
+
{
|
|
365
|
+
"query": s.body(QueryTypes.postQueryBodySchema),
|
|
366
|
+
"token": s.auth(Bearer),
|
|
367
|
+
},
|
|
364
368
|
responses: [s => s.data(ResponseTypes.queryResponseSchema)],
|
|
365
369
|
})
|
|
366
370
|
|
|
367
371
|
let heightRoute = Rest.route(() => {
|
|
368
372
|
path: "/height",
|
|
369
373
|
method: Get,
|
|
370
|
-
input:
|
|
374
|
+
input: s => s.auth(Bearer),
|
|
371
375
|
responses: [s => s.field("height", S.int)],
|
|
372
376
|
})
|
package/src/sources/Rpc.res
CHANGED
|
@@ -71,7 +71,7 @@ module GetLogs = {
|
|
|
71
71
|
topics->Belt.Array.map(toTopicFilter)
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
let mapTopicQuery = ({topic0, topic1, topic2, topic3}:
|
|
74
|
+
let mapTopicQuery = ({topic0, topic1, topic2, topic3}: Internal.topicSelection): topicQuery =>
|
|
75
75
|
makeTopicQuery(~topic0, ~topic1, ~topic2, ~topic3)
|
|
76
76
|
|
|
77
77
|
type param = {
|