envio 2.15.0 → 2.16.1
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/package.json +5 -5
- package/src/Internal.res +18 -8
- package/src/LogSelection.res +103 -33
- package/src/Utils.res +29 -0
- package/src/bindings/Ethers.res +11 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "envio",
|
|
3
|
-
"version": "v2.
|
|
3
|
+
"version": "v2.16.1",
|
|
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.16.1",
|
|
27
|
+
"envio-linux-arm64": "v2.16.1",
|
|
28
|
+
"envio-darwin-x64": "v2.16.1",
|
|
29
|
+
"envio-darwin-arm64": "v2.16.1"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@envio-dev/hypersync-client": "0.6.3",
|
package/src/Internal.res
CHANGED
|
@@ -64,11 +64,18 @@ type genericHandlerWithLoader<'loader, 'handler, 'eventFilters> = {
|
|
|
64
64
|
preRegisterDynamicContracts?: bool,
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
// This is private so it's not manually constructed internally
|
|
68
|
+
// The idea is that it can only be coerced from fuel/evmEventConfig
|
|
69
|
+
// and it can include their fields. We prevent manual creation,
|
|
70
|
+
// so the fields are not overwritten and we can safely cast the type back to fuel/evmEventConfig
|
|
71
|
+
type eventConfig = private {
|
|
68
72
|
id: string,
|
|
69
73
|
name: string,
|
|
70
74
|
contractName: string,
|
|
71
75
|
isWildcard: bool,
|
|
76
|
+
// Usually always false for wildcard events
|
|
77
|
+
// But might be true for wildcard event with dynamic event filter by addresses
|
|
78
|
+
dependsOnAddresses: bool,
|
|
72
79
|
preRegisterDynamicContracts: bool,
|
|
73
80
|
loader: option<loader>,
|
|
74
81
|
handler: option<handler>,
|
|
@@ -83,7 +90,7 @@ type fuelEventKind =
|
|
|
83
90
|
| Transfer
|
|
84
91
|
| Call
|
|
85
92
|
type fuelEventConfig = {
|
|
86
|
-
...
|
|
93
|
+
...eventConfig,
|
|
87
94
|
kind: fuelEventKind,
|
|
88
95
|
}
|
|
89
96
|
type fuelContractConfig = {
|
|
@@ -98,21 +105,26 @@ type topicSelection = {
|
|
|
98
105
|
topic3: array<EvmTypes.Hex.t>,
|
|
99
106
|
}
|
|
100
107
|
|
|
108
|
+
type eventFiltersArgs = {chainId: int, addresses: array<Address.t>}
|
|
109
|
+
|
|
110
|
+
type eventFilters =
|
|
111
|
+
Static(array<topicSelection>) | Dynamic(array<Address.t> => array<topicSelection>)
|
|
112
|
+
|
|
101
113
|
type evmEventConfig = {
|
|
102
|
-
...
|
|
103
|
-
|
|
114
|
+
...eventConfig,
|
|
115
|
+
getEventFiltersOrThrow: ChainMap.Chain.t => eventFilters,
|
|
104
116
|
blockSchema: S.schema<eventBlock>,
|
|
105
117
|
transactionSchema: S.schema<eventTransaction>,
|
|
106
118
|
convertHyperSyncEventArgs: HyperSyncClient.Decoder.decodedEvent => eventParams,
|
|
107
119
|
}
|
|
108
120
|
type evmContractConfig = {
|
|
109
121
|
name: string,
|
|
110
|
-
abi:
|
|
122
|
+
abi: EvmTypes.Abi.t,
|
|
111
123
|
events: array<evmEventConfig>,
|
|
112
124
|
}
|
|
113
125
|
|
|
114
126
|
type eventItem = {
|
|
115
|
-
eventConfig:
|
|
127
|
+
eventConfig: eventConfig,
|
|
116
128
|
timestamp: int,
|
|
117
129
|
chain: ChainMap.Chain.t,
|
|
118
130
|
blockNumber: int,
|
|
@@ -149,5 +161,3 @@ type entity = private {id: string}
|
|
|
149
161
|
|
|
150
162
|
@genType.import(("./bindings/OpaqueTypes.ts", "invalid"))
|
|
151
163
|
type noEventFilters
|
|
152
|
-
type eventFilters
|
|
153
|
-
type eventFiltersArgs = {chainId: int}
|
package/src/LogSelection.res
CHANGED
|
@@ -57,53 +57,123 @@ let make = (~addresses, ~topicSelections) => {
|
|
|
57
57
|
{addresses, topicSelections}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
type parsedEventFilters = {
|
|
61
|
+
getEventFiltersOrThrow: ChainMap.Chain.t => Internal.eventFilters,
|
|
62
|
+
dependsOnAddresses: bool,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let parseEventFiltersOrThrow = {
|
|
61
66
|
let emptyTopics = []
|
|
62
67
|
let noopGetter = _ => emptyTopics
|
|
63
68
|
|
|
64
69
|
(
|
|
65
|
-
~chain,
|
|
66
70
|
~eventFilters: option<Js.Json.t>,
|
|
67
71
|
~sighash,
|
|
72
|
+
~params,
|
|
68
73
|
~topic1=noopGetter,
|
|
69
74
|
~topic2=noopGetter,
|
|
70
75
|
~topic3=noopGetter,
|
|
71
|
-
) => {
|
|
76
|
+
): parsedEventFilters => {
|
|
77
|
+
let dependsOnAddresses = ref(false)
|
|
72
78
|
let topic0 = [sighash->EvmTypes.Hex.fromStringUnsafe]
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
}
|
|
79
|
+
let default = {
|
|
80
|
+
Internal.topic0,
|
|
81
|
+
topic1: emptyTopics,
|
|
82
|
+
topic2: emptyTopics,
|
|
83
|
+
topic3: emptyTopics,
|
|
84
|
+
}
|
|
90
85
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
86
|
+
let parse = (eventFilters: Js.Json.t): array<Internal.topicSelection> => {
|
|
87
|
+
switch eventFilters {
|
|
88
|
+
| Array([]) => [%raw(`{}`)]
|
|
89
|
+
| Array(a) => a
|
|
90
|
+
| _ => [eventFilters]
|
|
91
|
+
}->Js.Array2.map(eventFilter => {
|
|
92
|
+
switch eventFilter {
|
|
93
|
+
| Object(eventFilter) => {
|
|
94
|
+
let filterKeys = eventFilter->Js.Dict.keys
|
|
95
|
+
switch filterKeys {
|
|
96
|
+
| [] => default
|
|
97
|
+
| _ => {
|
|
98
|
+
filterKeys->Js.Array2.forEach(key => {
|
|
99
|
+
if params->Js.Array2.includes(key)->not {
|
|
100
|
+
// In TS type validation doesn't catch this
|
|
101
|
+
// when we have eventFilters as a callback
|
|
102
|
+
Js.Exn.raiseError(
|
|
103
|
+
`Invalid event filters configuration. The event doesn't have an indexed parameter "${key}" and can't use it for filtering`,
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
{
|
|
108
|
+
Internal.topic0,
|
|
109
|
+
topic1: topic1(eventFilter),
|
|
110
|
+
topic2: topic2(eventFilter),
|
|
111
|
+
topic3: topic3(eventFilter),
|
|
112
|
+
}
|
|
113
|
+
}
|
|
102
114
|
}
|
|
103
|
-
| _ => Js.Exn.raiseError("Invalid event filters configuration. Expected an object")
|
|
104
115
|
}
|
|
105
|
-
|
|
116
|
+
| _ => Js.Exn.raiseError("Invalid event filters configuration. Expected an object")
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
let getEventFiltersOrThrow = switch eventFilters {
|
|
122
|
+
| None => {
|
|
123
|
+
let static: Internal.eventFilters = Static([default])
|
|
124
|
+
_ => static
|
|
125
|
+
}
|
|
126
|
+
| Some(eventFilters) =>
|
|
127
|
+
if Js.typeof(eventFilters) === "function" {
|
|
128
|
+
let fn = eventFilters->(Utils.magic: Js.Json.t => Internal.eventFiltersArgs => Js.Json.t)
|
|
129
|
+
// When user passess a function to event filters we need to
|
|
130
|
+
// first determine whether it uses addresses or not
|
|
131
|
+
// Because the fetching logic will be different for wildcard events
|
|
132
|
+
// 1. If wildcard event doesn't use addresses,
|
|
133
|
+
// it should start fetching even without static addresses in the config
|
|
134
|
+
// 2. If wildcard event uses addresses in event filters,
|
|
135
|
+
// it should first wait for dynamic contract registration
|
|
136
|
+
// So to deterimine which case we run the function with dummy args
|
|
137
|
+
// and check if it uses addresses by using the getter.
|
|
138
|
+
try {
|
|
139
|
+
let args = (
|
|
140
|
+
{
|
|
141
|
+
chainId: 0,
|
|
142
|
+
addresses: [],
|
|
143
|
+
}: Internal.eventFiltersArgs
|
|
144
|
+
)->Utils.Object.defineProperty(
|
|
145
|
+
"addresses",
|
|
146
|
+
{
|
|
147
|
+
get: () => {
|
|
148
|
+
dependsOnAddresses := true
|
|
149
|
+
[]
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
)
|
|
153
|
+
let _ = fn(args)
|
|
154
|
+
} catch {
|
|
155
|
+
| _ => ()
|
|
156
|
+
}
|
|
157
|
+
if dependsOnAddresses.contents {
|
|
158
|
+
chain => Internal.Dynamic(
|
|
159
|
+
addresses => fn({chainId: chain->ChainMap.Chain.toChainId, addresses})->parse,
|
|
160
|
+
)
|
|
161
|
+
} else {
|
|
162
|
+
// When we don't depend on addresses, can mark the event filter
|
|
163
|
+
// as static and avoid recalculating on every batch
|
|
164
|
+
chain => Internal.Static(
|
|
165
|
+
fn({chainId: chain->ChainMap.Chain.toChainId, addresses: []})->parse,
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
} else {
|
|
169
|
+
let static: Internal.eventFilters = Static(eventFilters->parse)
|
|
170
|
+
_ => static
|
|
106
171
|
}
|
|
107
172
|
}
|
|
173
|
+
|
|
174
|
+
{
|
|
175
|
+
getEventFiltersOrThrow,
|
|
176
|
+
dependsOnAddresses: dependsOnAddresses.contents,
|
|
177
|
+
}
|
|
108
178
|
}
|
|
109
179
|
}
|
package/src/Utils.res
CHANGED
|
@@ -7,6 +7,21 @@ let delay = milliseconds =>
|
|
|
7
7
|
}, milliseconds)
|
|
8
8
|
})
|
|
9
9
|
|
|
10
|
+
module Object = {
|
|
11
|
+
// Define a type for the property descriptor
|
|
12
|
+
type propertyDescriptor<'a> = {
|
|
13
|
+
configurable?: bool,
|
|
14
|
+
enumerable?: bool,
|
|
15
|
+
writable?: bool,
|
|
16
|
+
value?: 'a,
|
|
17
|
+
get?: unit => 'a,
|
|
18
|
+
set?: 'a => unit,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@val @scope("Object")
|
|
22
|
+
external defineProperty: ('obj, string, propertyDescriptor<'a>) => 'obj = "defineProperty"
|
|
23
|
+
}
|
|
24
|
+
|
|
10
25
|
module Option = {
|
|
11
26
|
let mapNone = (opt: option<'a>, val: 'b): option<'b> => {
|
|
12
27
|
switch opt {
|
|
@@ -50,6 +65,20 @@ module Dict = {
|
|
|
50
65
|
*/
|
|
51
66
|
external dangerouslyGetNonOption: (dict<'a>, string) => option<'a> = ""
|
|
52
67
|
|
|
68
|
+
let push = (dict, key, value) => {
|
|
69
|
+
switch dict->dangerouslyGetNonOption(key) {
|
|
70
|
+
| Some(arr) => arr->Js.Array2.push(value)->ignore
|
|
71
|
+
| None => dict->Js.Dict.set(key, [value])
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let pushMany = (dict, key, values) => {
|
|
76
|
+
switch dict->dangerouslyGetNonOption(key) {
|
|
77
|
+
| Some(arr) => arr->Js.Array2.pushMany(values)->ignore
|
|
78
|
+
| None => dict->Js.Dict.set(key, values)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
53
82
|
let merge: (dict<'a>, dict<'a>) => dict<'a> = %raw(`(dictA, dictB) => ({...dictA, ...dictB})`)
|
|
54
83
|
|
|
55
84
|
let map = (dict, fn) => {
|
package/src/bindings/Ethers.res
CHANGED
|
@@ -55,7 +55,7 @@ module CombinedFilter = {
|
|
|
55
55
|
type combinedFilterRecord = {
|
|
56
56
|
address?: array<Address.t>,
|
|
57
57
|
//The second element of the tuple is the
|
|
58
|
-
topics:
|
|
58
|
+
topics: Rpc.GetLogs.topicQuery,
|
|
59
59
|
fromBlock: int,
|
|
60
60
|
toBlock: int,
|
|
61
61
|
}
|
|
@@ -147,17 +147,18 @@ module JsonRpcProvider = {
|
|
|
147
147
|
@send
|
|
148
148
|
external getTransaction: (t, ~transactionHash: string) => promise<transaction> = "getTransaction"
|
|
149
149
|
|
|
150
|
-
let makeGetTransactionFields = (~getTransactionByHash) =>
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
150
|
+
let makeGetTransactionFields = (~getTransactionByHash) =>
|
|
151
|
+
async (log: log): promise<unknown> => {
|
|
152
|
+
let transaction = await getTransactionByHash(log.transactionHash)
|
|
153
|
+
// Mutating should be fine, since the transaction isn't used anywhere else outside the function
|
|
154
|
+
let fields: {..} = transaction->Obj.magic
|
|
154
155
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
156
|
+
// Make it compatible with HyperSync transaction fields
|
|
157
|
+
fields["transactionIndex"] = log.transactionIndex
|
|
158
|
+
fields["input"] = fields["data"]
|
|
158
159
|
|
|
159
|
-
|
|
160
|
-
|
|
160
|
+
fields->Obj.magic
|
|
161
|
+
}
|
|
161
162
|
|
|
162
163
|
type block = {
|
|
163
164
|
_difficulty: bigint,
|