envio 2.7.2 → 2.7.4
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 +9 -6
- package/rescript.json +7 -2
- package/src/Address.res +29 -0
- package/src/EvmTypes.res +7 -0
- package/src/Throttler.res +55 -0
- package/src/Utils.res +346 -0
- package/src/bindings/HyperSyncClient.res +481 -0
- package/src/bindings/Pino.res +175 -0
- package/src/bindings/Viem.res +28 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "envio",
|
|
3
|
-
"version": "v2.7.
|
|
3
|
+
"version": "v2.7.4",
|
|
4
4
|
"description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
|
|
5
5
|
"bin": "./bin.js",
|
|
6
6
|
"repository": {
|
|
@@ -23,13 +23,16 @@
|
|
|
23
23
|
},
|
|
24
24
|
"homepage": "https://envio.dev",
|
|
25
25
|
"optionalDependencies": {
|
|
26
|
-
"envio-linux-x64": "v2.7.
|
|
27
|
-
"envio-linux-arm64": "v2.7.
|
|
28
|
-
"envio-darwin-x64": "v2.7.
|
|
29
|
-
"envio-darwin-arm64": "v2.7.
|
|
26
|
+
"envio-linux-x64": "v2.7.4",
|
|
27
|
+
"envio-linux-arm64": "v2.7.4",
|
|
28
|
+
"envio-darwin-x64": "v2.7.4",
|
|
29
|
+
"envio-darwin-arm64": "v2.7.4"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"
|
|
32
|
+
"@envio-dev/hypersync-client": "0.6.2",
|
|
33
|
+
"rescript": "11.1.3",
|
|
34
|
+
"rescript-schema": "8.2.0",
|
|
35
|
+
"viem": "2.21.0"
|
|
33
36
|
},
|
|
34
37
|
"files": [
|
|
35
38
|
"bin.js",
|
package/rescript.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "envio",
|
|
3
|
-
"namespace":
|
|
3
|
+
"namespace": false,
|
|
4
4
|
"sources": [
|
|
5
5
|
{
|
|
6
6
|
"dir": "src",
|
|
@@ -11,5 +11,10 @@
|
|
|
11
11
|
"package-specs": {
|
|
12
12
|
"module": "commonjs",
|
|
13
13
|
"in-source": true
|
|
14
|
-
}
|
|
14
|
+
},
|
|
15
|
+
"gentypeconfig": {
|
|
16
|
+
"generatedFileExtension": ".gen.ts"
|
|
17
|
+
},
|
|
18
|
+
"bs-dependencies": ["rescript-schema"],
|
|
19
|
+
"bsc-flags": ["-open RescriptSchema"]
|
|
15
20
|
}
|
package/src/Address.res
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
@genType.import(("./bindings/OpaqueTypes.ts", "Address"))
|
|
2
|
+
type t
|
|
3
|
+
|
|
4
|
+
let schema = S.string->S.setName("Address")->(Utils.magic: S.t<string> => S.t<t>)
|
|
5
|
+
|
|
6
|
+
external toString: t => string = "%identity"
|
|
7
|
+
|
|
8
|
+
external unsafeFromString: string => t = "%identity"
|
|
9
|
+
|
|
10
|
+
module Evm = {
|
|
11
|
+
@module("viem")
|
|
12
|
+
external fromStringOrThrow: string => t = "getAddress"
|
|
13
|
+
// Reassign since the function might be used in the handler code
|
|
14
|
+
// and we don't want to have a "viem" import there. It's needed to keep "viem" a dependency
|
|
15
|
+
// of generated code instead of adding it to the indexer project dependencies.
|
|
16
|
+
// Also, we want a custom error message, which is searchable in our codebase.
|
|
17
|
+
let fromStringOrThrow = string => {
|
|
18
|
+
try {
|
|
19
|
+
fromStringOrThrow(string)
|
|
20
|
+
} catch {
|
|
21
|
+
| _ =>
|
|
22
|
+
Js.Exn.raiseError(
|
|
23
|
+
`Address "${string}" is invalid. Expected a 20-byte hex string starting with 0x.`,
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let fromAddressOrThrow = address => address->toString->fromStringOrThrow
|
|
29
|
+
}
|
package/src/EvmTypes.res
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
module Hex = {
|
|
2
2
|
type t
|
|
3
|
+
/**No string validation in schema*/
|
|
4
|
+
let schema =
|
|
5
|
+
S.string->S.setName("EVM.Hex")->(Utils.magic: S.t<string> => S.t<t>)
|
|
3
6
|
external fromStringUnsafe: string => t = "%identity"
|
|
4
7
|
external fromStringsUnsafe: array<string> => array<t> = "%identity"
|
|
5
8
|
external toString: t => string = "%identity"
|
|
6
9
|
external toStrings: array<t> => array<string> = "%identity"
|
|
7
10
|
}
|
|
11
|
+
|
|
12
|
+
module Abi = {
|
|
13
|
+
type t
|
|
14
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
type t = {
|
|
2
|
+
mutable lastRunTimeMillis: float,
|
|
3
|
+
mutable isRunning: bool,
|
|
4
|
+
mutable isAwaitingInterval: bool,
|
|
5
|
+
mutable scheduled: option<unit => promise<unit>>,
|
|
6
|
+
intervalMillis: float,
|
|
7
|
+
logger: Pino.t,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let make = (~intervalMillis: int, ~logger) => {
|
|
11
|
+
lastRunTimeMillis: 0.,
|
|
12
|
+
isRunning: false,
|
|
13
|
+
isAwaitingInterval: false,
|
|
14
|
+
scheduled: None,
|
|
15
|
+
intervalMillis: intervalMillis->Belt.Int.toFloat,
|
|
16
|
+
logger,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let rec startInternal = async (throttler: t) => {
|
|
20
|
+
switch throttler {
|
|
21
|
+
| {scheduled: Some(fn), isRunning: false, isAwaitingInterval: false} =>
|
|
22
|
+
let timeSinceLastRun = Js.Date.now() -. throttler.lastRunTimeMillis
|
|
23
|
+
|
|
24
|
+
//Only execute if we are passed the interval
|
|
25
|
+
if timeSinceLastRun >= throttler.intervalMillis {
|
|
26
|
+
throttler.isRunning = true
|
|
27
|
+
throttler.scheduled = None
|
|
28
|
+
throttler.lastRunTimeMillis = Js.Date.now()
|
|
29
|
+
|
|
30
|
+
switch await fn() {
|
|
31
|
+
| exception exn =>
|
|
32
|
+
throttler.logger->Pino.errorExn(
|
|
33
|
+
Pino.createPinoMessageWithError("Scheduled action failed in throttler", exn),
|
|
34
|
+
)
|
|
35
|
+
| _ => ()
|
|
36
|
+
}
|
|
37
|
+
throttler.isRunning = false
|
|
38
|
+
|
|
39
|
+
await throttler->startInternal
|
|
40
|
+
} else {
|
|
41
|
+
//Store isAwaitingInterval in state so that timers don't continuously get created
|
|
42
|
+
throttler.isAwaitingInterval = true
|
|
43
|
+
let _ = Js.Global.setTimeout(() => {
|
|
44
|
+
throttler.isAwaitingInterval = false
|
|
45
|
+
throttler->startInternal->ignore
|
|
46
|
+
}, Belt.Int.fromFloat(throttler.intervalMillis -. timeSinceLastRun))
|
|
47
|
+
}
|
|
48
|
+
| _ => ()
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let schedule = (throttler: t, fn) => {
|
|
53
|
+
throttler.scheduled = Some(fn)
|
|
54
|
+
throttler->startInternal->ignore
|
|
55
|
+
}
|
package/src/Utils.res
ADDED
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
external magic: 'a => 'b = "%identity"
|
|
2
|
+
|
|
3
|
+
module Option = {
|
|
4
|
+
let mapNone = (opt: option<'a>, val: 'b): option<'b> => {
|
|
5
|
+
switch opt {
|
|
6
|
+
| None => Some(val)
|
|
7
|
+
| Some(_) => None
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let catchToNone: (unit => 'a) => option<'a> = unsafeFunc => {
|
|
12
|
+
try {
|
|
13
|
+
unsafeFunc()->Some
|
|
14
|
+
} catch {
|
|
15
|
+
| _ => None
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let flatten = opt =>
|
|
20
|
+
switch opt {
|
|
21
|
+
| None => None
|
|
22
|
+
| Some(opt) => opt
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let getExn = (opt, message) => {
|
|
26
|
+
switch opt {
|
|
27
|
+
| None => Js.Exn.raiseError(message)
|
|
28
|
+
| Some(v) => v
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module Tuple = {
|
|
34
|
+
/**Access a tuple value by its index*/
|
|
35
|
+
@warning("-27")
|
|
36
|
+
let get = (tuple: 'a, index: int): option<'b> => %raw(`tuple[index]`)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module Dict = {
|
|
40
|
+
@get_index
|
|
41
|
+
/**
|
|
42
|
+
It's the same as `Js.Dict.get` but it doesn't have runtime overhead to check if the key exists.
|
|
43
|
+
*/
|
|
44
|
+
external dangerouslyGetNonOption: (dict<'a>, string) => option<'a> = ""
|
|
45
|
+
|
|
46
|
+
let merge: (dict<'a>, dict<'a>) => dict<'a> = %raw(`(dictA, dictB) => ({...dictA, ...dictB})`)
|
|
47
|
+
|
|
48
|
+
let updateImmutable: (
|
|
49
|
+
dict<'a>,
|
|
50
|
+
string,
|
|
51
|
+
'a,
|
|
52
|
+
) => dict<'a> = %raw(`(dict, key, value) => ({...dict, [key]: value})`)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module Math = {
|
|
56
|
+
let minOptInt = (a, b) =>
|
|
57
|
+
switch (a, b) {
|
|
58
|
+
| (Some(a), Some(b)) => Pervasives.min(a, b)->Some
|
|
59
|
+
| (Some(a), None) => Some(a)
|
|
60
|
+
| (None, Some(b)) => Some(b)
|
|
61
|
+
| (None, None) => None
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
module Array = {
|
|
65
|
+
@val external jsArrayCreate: int => array<'a> = "Array"
|
|
66
|
+
|
|
67
|
+
/* Given a comaprator and two sorted lists, combine them into a single sorted list */
|
|
68
|
+
let mergeSorted = (f: ('a, 'a) => bool, xs: array<'a>, ys: array<'a>) => {
|
|
69
|
+
if Array.length(xs) == 0 {
|
|
70
|
+
ys
|
|
71
|
+
} else if Array.length(ys) == 0 {
|
|
72
|
+
xs
|
|
73
|
+
} else {
|
|
74
|
+
let n = Array.length(xs) + Array.length(ys)
|
|
75
|
+
let result = jsArrayCreate(n)
|
|
76
|
+
|
|
77
|
+
let rec loop = (i, j, k) => {
|
|
78
|
+
if i < Array.length(xs) && j < Array.length(ys) {
|
|
79
|
+
if f(xs[i], ys[j]) {
|
|
80
|
+
result[k] = xs[i]
|
|
81
|
+
loop(i + 1, j, k + 1)
|
|
82
|
+
} else {
|
|
83
|
+
result[k] = ys[j]
|
|
84
|
+
loop(i, j + 1, k + 1)
|
|
85
|
+
}
|
|
86
|
+
} else if i < Array.length(xs) {
|
|
87
|
+
result[k] = xs[i]
|
|
88
|
+
loop(i + 1, j, k + 1)
|
|
89
|
+
} else if j < Array.length(ys) {
|
|
90
|
+
result[k] = ys[j]
|
|
91
|
+
loop(i, j + 1, k + 1)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
loop(0, 0, 0)
|
|
96
|
+
result
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
Creates a shallow copy of the array and sets the value at the given index
|
|
102
|
+
*/
|
|
103
|
+
let setIndexImmutable = (arr: array<'a>, index: int, value: 'a): array<'a> => {
|
|
104
|
+
let shallowCopy = arr->Belt.Array.copy
|
|
105
|
+
shallowCopy->Js.Array2.unsafe_set(index, value)
|
|
106
|
+
shallowCopy
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
let transposeResults = (results: array<result<'a, 'b>>): result<array<'a>, 'b> => {
|
|
110
|
+
let rec loop = (index: int, output: array<'a>): result<array<'a>, 'b> => {
|
|
111
|
+
if index >= Array.length(results) {
|
|
112
|
+
Ok(output)
|
|
113
|
+
} else {
|
|
114
|
+
switch results->Js.Array2.unsafe_get(index) {
|
|
115
|
+
| Ok(value) => {
|
|
116
|
+
output[index] = value
|
|
117
|
+
loop(index + 1, output)
|
|
118
|
+
}
|
|
119
|
+
| Error(_) as err => err->(magic: result<'a, 'b> => result<array<'a>, 'b>)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
loop(0, Belt.Array.makeUninitializedUnsafe(results->Js.Array2.length))
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
Helper to check if a value exists in an array
|
|
129
|
+
*/
|
|
130
|
+
let includes = (arr: array<'a>, val: 'a) =>
|
|
131
|
+
arr->Js.Array2.find(item => item == val)->Belt.Option.isSome
|
|
132
|
+
|
|
133
|
+
let isEmpty = (arr: array<_>) =>
|
|
134
|
+
switch arr {
|
|
135
|
+
| [] => true
|
|
136
|
+
| _ => false
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
let awaitEach = async (arr: array<'a>, fn: 'a => promise<unit>) => {
|
|
140
|
+
for i in 0 to arr->Array.length - 1 {
|
|
141
|
+
let item = arr[i]
|
|
142
|
+
await item->fn
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
Creates a new array removing the item at the given index
|
|
148
|
+
|
|
149
|
+
Index > array length or < 0 results in a copy of the array
|
|
150
|
+
*/
|
|
151
|
+
let removeAtIndex = (array, index) => {
|
|
152
|
+
if index < 0 || index >= array->Array.length {
|
|
153
|
+
array->Array.copy
|
|
154
|
+
} else {
|
|
155
|
+
let head = array->Js.Array2.slice(~start=0, ~end_=index)
|
|
156
|
+
let tail = array->Belt.Array.sliceToEnd(index + 1)
|
|
157
|
+
[...head, ...tail]
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let last = (arr: array<'a>): option<'a> => arr->Belt.Array.get(arr->Array.length - 1)
|
|
162
|
+
|
|
163
|
+
let findReverseWithIndex = (arr: array<'a>, fn: 'a => bool): option<('a, int)> => {
|
|
164
|
+
let rec loop = (index: int) => {
|
|
165
|
+
if index < 0 {
|
|
166
|
+
None
|
|
167
|
+
} else {
|
|
168
|
+
let item = arr[index]
|
|
169
|
+
if fn(item) {
|
|
170
|
+
Some((item, index))
|
|
171
|
+
} else {
|
|
172
|
+
loop(index - 1)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
loop(arr->Array.length - 1)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
Currently a bug in rescript if you ignore the return value of spliceInPlace
|
|
181
|
+
https://github.com/rescript-lang/rescript-compiler/issues/6991
|
|
182
|
+
*/
|
|
183
|
+
@send
|
|
184
|
+
external spliceInPlace: (array<'a>, ~pos: int, ~remove: int) => array<'a> = "splice"
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
module String = {
|
|
188
|
+
let capitalize = str => {
|
|
189
|
+
str->Js.String2.slice(~from=0, ~to_=1)->Js.String.toUpperCase ++
|
|
190
|
+
str->Js.String2.sliceToEnd(~from=1)
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
Useful when an unsafe unwrap is needed on Result type
|
|
196
|
+
and Error holds an exn. This is better than Result.getExn
|
|
197
|
+
because the excepion is not just NOT_FOUND but will rather
|
|
198
|
+
bet the actual underlying exn
|
|
199
|
+
*/
|
|
200
|
+
let unwrapResultExn = res =>
|
|
201
|
+
switch res {
|
|
202
|
+
| Ok(v) => v
|
|
203
|
+
| Error(exn) => exn->raise
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
external queueMicrotask: (unit => unit) => unit = "queueMicrotask"
|
|
207
|
+
|
|
208
|
+
module Schema = {
|
|
209
|
+
let getNonOptionalFieldNames = schema => {
|
|
210
|
+
let acc = []
|
|
211
|
+
switch schema->S.classify {
|
|
212
|
+
| Object({items}) =>
|
|
213
|
+
items->Js.Array2.forEach(item => {
|
|
214
|
+
switch item.schema->S.classify {
|
|
215
|
+
// Check for null, since we generate S.null schema for db serializing
|
|
216
|
+
// In the future it should be changed to Option
|
|
217
|
+
| Null(_) => ()
|
|
218
|
+
| _ => acc->Js.Array2.push(item.location)->ignore
|
|
219
|
+
}
|
|
220
|
+
})
|
|
221
|
+
| _ => ()
|
|
222
|
+
}
|
|
223
|
+
acc
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
let getCapitalizedFieldNames = schema => {
|
|
227
|
+
switch schema->S.classify {
|
|
228
|
+
| Object({items}) => items->Js.Array2.map(item => item.location->String.capitalize)
|
|
229
|
+
| _ => []
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// When trying to serialize data to Json pg type, it will fail with
|
|
234
|
+
// PostgresError: column "params" is of type json but expression is of type boolean
|
|
235
|
+
// If there's bool or null on the root level. It works fine as object field values.
|
|
236
|
+
let coerceToJsonPgType = schema => {
|
|
237
|
+
schema->S.preprocess(s => {
|
|
238
|
+
switch s.schema->S.classify {
|
|
239
|
+
| Literal(Null(_))
|
|
240
|
+
| // This is a workaround for Fuel Bytes type
|
|
241
|
+
Unknown => {serializer: _ => %raw(`"null"`)}
|
|
242
|
+
| Null(_)
|
|
243
|
+
| Bool => {
|
|
244
|
+
serializer: unknown => {
|
|
245
|
+
if unknown === %raw(`null`) {
|
|
246
|
+
%raw(`"null"`)
|
|
247
|
+
} else if unknown === %raw(`false`) {
|
|
248
|
+
%raw(`"false"`)
|
|
249
|
+
} else if unknown === %raw(`true`) {
|
|
250
|
+
%raw(`"true"`)
|
|
251
|
+
} else {
|
|
252
|
+
unknown
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
}
|
|
256
|
+
| _ => {}
|
|
257
|
+
}
|
|
258
|
+
})
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
module Set = {
|
|
263
|
+
type t<'value>
|
|
264
|
+
|
|
265
|
+
/*
|
|
266
|
+
* Constructor
|
|
267
|
+
*/
|
|
268
|
+
@ocaml.doc("Creates a new `Set` object.") @new
|
|
269
|
+
external make: unit => t<'value> = "Set"
|
|
270
|
+
|
|
271
|
+
@ocaml.doc("Creates a new `Set` object.") @new
|
|
272
|
+
external fromEntries: array<'value> => t<'value> = "Set"
|
|
273
|
+
|
|
274
|
+
/*
|
|
275
|
+
* Instance properties
|
|
276
|
+
*/
|
|
277
|
+
@ocaml.doc("Returns the number of values in the `Set` object.") @get
|
|
278
|
+
external size: t<'value> => int = "size"
|
|
279
|
+
|
|
280
|
+
/*
|
|
281
|
+
* Instance methods
|
|
282
|
+
*/
|
|
283
|
+
@ocaml.doc("Appends `value` to the `Set` object. Returns the `Set` object with added value.")
|
|
284
|
+
@send
|
|
285
|
+
external add: (t<'value>, 'value) => t<'value> = "add"
|
|
286
|
+
|
|
287
|
+
@ocaml.doc("Removes all elements from the `Set` object.") @send
|
|
288
|
+
external clear: t<'value> => unit = "clear"
|
|
289
|
+
|
|
290
|
+
@ocaml.doc(
|
|
291
|
+
"Removes the element associated to the `value` and returns a boolean asserting whether an element was successfully removed or not. `Set.prototype.has(value)` will return `false` afterwards."
|
|
292
|
+
)
|
|
293
|
+
@send
|
|
294
|
+
external delete: (t<'value>, 'value) => bool = "delete"
|
|
295
|
+
|
|
296
|
+
@ocaml.doc(
|
|
297
|
+
"Returns a boolean asserting whether an element is present with the given value in the `Set` object or not."
|
|
298
|
+
)
|
|
299
|
+
@send
|
|
300
|
+
external has: (t<'value>, 'value) => bool = "has"
|
|
301
|
+
|
|
302
|
+
external toArray: t<'a> => array<'a> = "Array.from"
|
|
303
|
+
|
|
304
|
+
/*
|
|
305
|
+
* Iteration methods
|
|
306
|
+
*/
|
|
307
|
+
/*
|
|
308
|
+
/// NOTE - if we need iteration we can add this back - currently it requires the `rescript-js-iterator` library.
|
|
309
|
+
@ocaml.doc(
|
|
310
|
+
"Returns a new iterator object that yields the **values** for each element in the `Set` object in insertion order."
|
|
311
|
+
)
|
|
312
|
+
@send
|
|
313
|
+
external values: t<'value> => Js_iterator.t<'value> = "values"
|
|
314
|
+
|
|
315
|
+
@ocaml.doc("An alias for `Set.prototype.values()`.") @send
|
|
316
|
+
external keys: t<'value> => Js_iterator.t<'value> = "values"
|
|
317
|
+
|
|
318
|
+
@ocaml.doc("Returns a new iterator object that contains **an array of [value, value]** for each element in the `Set` object, in insertion order.
|
|
319
|
+
|
|
320
|
+
This is similar to the [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, so that each entry's `key` is the same as its `value` for a `Set`.")
|
|
321
|
+
@send
|
|
322
|
+
external entries: t<'value> => Js_iterator.t<('value, 'value)> = "entries"
|
|
323
|
+
*/
|
|
324
|
+
@ocaml.doc(
|
|
325
|
+
"Calls `callbackFn` once for each value present in the `Set` object, in insertion order."
|
|
326
|
+
)
|
|
327
|
+
@send
|
|
328
|
+
external forEach: (t<'value>, 'value => unit) => unit = "forEach"
|
|
329
|
+
|
|
330
|
+
@ocaml.doc(
|
|
331
|
+
"Calls `callbackFn` once for each value present in the `Set` object, in insertion order."
|
|
332
|
+
)
|
|
333
|
+
@send
|
|
334
|
+
external forEachWithSet: (t<'value>, ('value, 'value, t<'value>) => unit) => unit = "forEach"
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
module WeakMap = {
|
|
338
|
+
type t<'k, 'v> = Js.WeakMap.t<'k, 'v>
|
|
339
|
+
|
|
340
|
+
@new external make: unit => t<'k, 'v> = "WeakMap"
|
|
341
|
+
|
|
342
|
+
@send external get: (t<'k, 'v>, 'k) => option<'v> = "get"
|
|
343
|
+
@send external unsafeGet: (t<'k, 'v>, 'k) => 'v = "get"
|
|
344
|
+
@send external has: (t<'k, 'v>, 'k) => bool = "has"
|
|
345
|
+
@send external set: (t<'k, 'v>, 'k, 'v) => t<'k, 'v> = "set"
|
|
346
|
+
}
|
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
type cfg = {
|
|
2
|
+
url?: string,
|
|
3
|
+
bearerToken?: string,
|
|
4
|
+
httpReqTimeoutMillis?: int,
|
|
5
|
+
maxNumRetries?: int,
|
|
6
|
+
retryBackoffMs?: int,
|
|
7
|
+
retryBaseMs?: int,
|
|
8
|
+
retryCeilingMs?: int,
|
|
9
|
+
enableChecksumAddresses?: bool,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
module QueryTypes = {
|
|
13
|
+
type blockField =
|
|
14
|
+
| Number
|
|
15
|
+
| Hash
|
|
16
|
+
| ParentHash
|
|
17
|
+
| Nonce
|
|
18
|
+
| Sha3Uncles
|
|
19
|
+
| LogsBloom
|
|
20
|
+
| TransactionsRoot
|
|
21
|
+
| StateRoot
|
|
22
|
+
| ReceiptsRoot
|
|
23
|
+
| Miner
|
|
24
|
+
| Difficulty
|
|
25
|
+
| TotalDifficulty
|
|
26
|
+
| ExtraData
|
|
27
|
+
| Size
|
|
28
|
+
| GasLimit
|
|
29
|
+
| GasUsed
|
|
30
|
+
| Timestamp
|
|
31
|
+
| Uncles
|
|
32
|
+
| BaseFeePerGas
|
|
33
|
+
| BlobGasUsed
|
|
34
|
+
| ExcessBlobGas
|
|
35
|
+
| ParentBeaconBlockRoot
|
|
36
|
+
| WithdrawalsRoot
|
|
37
|
+
| Withdrawals
|
|
38
|
+
| L1BlockNumber
|
|
39
|
+
| SendCount
|
|
40
|
+
| SendRoot
|
|
41
|
+
| MixHash
|
|
42
|
+
|
|
43
|
+
type transactionField =
|
|
44
|
+
| BlockHash
|
|
45
|
+
| BlockNumber
|
|
46
|
+
| From
|
|
47
|
+
| Gas
|
|
48
|
+
| GasPrice
|
|
49
|
+
| Hash
|
|
50
|
+
| Input
|
|
51
|
+
| Nonce
|
|
52
|
+
| To
|
|
53
|
+
| TransactionIndex
|
|
54
|
+
| Value
|
|
55
|
+
| V
|
|
56
|
+
| R
|
|
57
|
+
| S
|
|
58
|
+
| YParity
|
|
59
|
+
| MaxPriorityFeePerGas
|
|
60
|
+
| MaxFeePerGas
|
|
61
|
+
| ChainId
|
|
62
|
+
| AccessList
|
|
63
|
+
| MaxFeePerBlobGas
|
|
64
|
+
| BlobVersionedHashes
|
|
65
|
+
| CumulativeGasUsed
|
|
66
|
+
| EffectiveGasPrice
|
|
67
|
+
| GasUsed
|
|
68
|
+
| ContractAddress
|
|
69
|
+
| LogsBloom
|
|
70
|
+
| Kind
|
|
71
|
+
| Root
|
|
72
|
+
| Status
|
|
73
|
+
| L1Fee
|
|
74
|
+
| L1GasPrice
|
|
75
|
+
| L1GasUsed
|
|
76
|
+
| L1FeeScalar
|
|
77
|
+
| GasUsedForL1
|
|
78
|
+
|
|
79
|
+
type logField =
|
|
80
|
+
| Removed
|
|
81
|
+
| LogIndex
|
|
82
|
+
| TransactionIndex
|
|
83
|
+
| TransactionHash
|
|
84
|
+
| BlockHash
|
|
85
|
+
| BlockNumber
|
|
86
|
+
| Address
|
|
87
|
+
| Data
|
|
88
|
+
| Topic0
|
|
89
|
+
| Topic1
|
|
90
|
+
| Topic2
|
|
91
|
+
| Topic3
|
|
92
|
+
|
|
93
|
+
type traceField =
|
|
94
|
+
| From
|
|
95
|
+
| To
|
|
96
|
+
| CallType
|
|
97
|
+
| Gas
|
|
98
|
+
| Input
|
|
99
|
+
| Init
|
|
100
|
+
| Value
|
|
101
|
+
| Author
|
|
102
|
+
| RewardType
|
|
103
|
+
| BlockHash
|
|
104
|
+
| BlockNumber
|
|
105
|
+
| Address
|
|
106
|
+
| Code
|
|
107
|
+
| GasUsed
|
|
108
|
+
| Output
|
|
109
|
+
| Subtraces
|
|
110
|
+
| TraceAddress
|
|
111
|
+
| TransactionHash
|
|
112
|
+
| TransactionPosition
|
|
113
|
+
| Kind
|
|
114
|
+
| Error
|
|
115
|
+
|
|
116
|
+
type fieldSelection = {
|
|
117
|
+
block?: array<blockField>,
|
|
118
|
+
transaction?: array<transactionField>,
|
|
119
|
+
log?: array<logField>,
|
|
120
|
+
trace?: array<traceField>,
|
|
121
|
+
}
|
|
122
|
+
type topicFilter = array<EvmTypes.Hex.t>
|
|
123
|
+
type topic0 = topicFilter
|
|
124
|
+
type topic1 = topicFilter
|
|
125
|
+
type topic2 = topicFilter
|
|
126
|
+
type topic3 = topicFilter
|
|
127
|
+
type topicSelection = (topic0, topic1, topic2, topic3)
|
|
128
|
+
let makeTopicSelection = (~topic0=[], ~topic1=[], ~topic2=[], ~topic3=[]) => (
|
|
129
|
+
topic0,
|
|
130
|
+
topic1,
|
|
131
|
+
topic2,
|
|
132
|
+
topic3,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
type logSelection = {
|
|
136
|
+
/**
|
|
137
|
+
* Address of the contract, any logs that has any of these addresses will be returned.
|
|
138
|
+
* Empty means match all.
|
|
139
|
+
*/
|
|
140
|
+
address?: array<Address.t>,
|
|
141
|
+
/**
|
|
142
|
+
* Topics to match, each member of the top level array is another array, if the nth topic matches any
|
|
143
|
+
* topic specified in topics[n] the log will be returned. Empty means match all.
|
|
144
|
+
*/
|
|
145
|
+
topics: topicSelection,
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
let makeLogSelection = (~address, ~topics) => {address, topics}
|
|
149
|
+
|
|
150
|
+
type transactionSelection = {
|
|
151
|
+
/**
|
|
152
|
+
* Address the transaction should originate from. If transaction.from matches any of these, the transaction
|
|
153
|
+
* will be returned. Keep in mind that this has an and relationship with to filter, so each transaction should
|
|
154
|
+
* match both of them. Empty means match all.
|
|
155
|
+
*/
|
|
156
|
+
from?: array<Address.t>,
|
|
157
|
+
/**
|
|
158
|
+
* Address the transaction should go to. If transaction.to matches any of these, the transaction will
|
|
159
|
+
* be returned. Keep in mind that this has an and relationship with from filter, so each transaction should
|
|
160
|
+
* match both of them. Empty means match all.
|
|
161
|
+
*/
|
|
162
|
+
@as("to")
|
|
163
|
+
to_?: array<Address.t>,
|
|
164
|
+
/** If first 4 bytes of transaction input matches any of these, transaction will be returned. Empty means match all. */
|
|
165
|
+
sighash?: array<string>,
|
|
166
|
+
/** If tx.status matches this it will be returned. */
|
|
167
|
+
status?: int,
|
|
168
|
+
/** If transaction.type matches any of these values, the transaction will be returned */
|
|
169
|
+
kind?: array<int>,
|
|
170
|
+
contractAddress?: array<Address.t>,
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
type traceSelection = {
|
|
174
|
+
from?: array<Address.t>,
|
|
175
|
+
@as("to") to_?: array<Address.t>,
|
|
176
|
+
address?: array<Address.t>,
|
|
177
|
+
callType?: array<string>,
|
|
178
|
+
rewardType?: array<string>,
|
|
179
|
+
kind?: array<string>,
|
|
180
|
+
sighash?: array<string>,
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
type blockSelection = {
|
|
184
|
+
/**
|
|
185
|
+
* Hash of a block, any blocks that have one of these hashes will be returned.
|
|
186
|
+
* Empty means match all.
|
|
187
|
+
*/
|
|
188
|
+
hash?: array<string>,
|
|
189
|
+
/**
|
|
190
|
+
* Miner address of a block, any blocks that have one of these miners will be returned.
|
|
191
|
+
* Empty means match all.
|
|
192
|
+
*/
|
|
193
|
+
miner?: array<Address.t>,
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
type joinMode = | @as(0) Default | @as(1) JoinAll | @as(2) JoinNothing
|
|
197
|
+
|
|
198
|
+
type query = {
|
|
199
|
+
/** The block to start the query from */
|
|
200
|
+
fromBlock: int,
|
|
201
|
+
/**
|
|
202
|
+
* The block to end the query at. If not specified, the query will go until the
|
|
203
|
+
* end of data. Exclusive, the returned range will be [from_block..to_block).
|
|
204
|
+
*
|
|
205
|
+
* The query will return before it reaches this target block if it hits the time limit
|
|
206
|
+
* configured on the server. The user should continue their query by putting the
|
|
207
|
+
* next_block field in the response into from_block field of their next query. This implements
|
|
208
|
+
* pagination.
|
|
209
|
+
*/
|
|
210
|
+
@as("toBlock")
|
|
211
|
+
toBlockExclusive?: int,
|
|
212
|
+
/**
|
|
213
|
+
* List of log selections, these have an or relationship between them, so the query will return logs
|
|
214
|
+
* that match any of these selections.
|
|
215
|
+
*/
|
|
216
|
+
logs?: array<logSelection>,
|
|
217
|
+
/**
|
|
218
|
+
* List of transaction selections, the query will return transactions that match any of these selections and
|
|
219
|
+
* it will return transactions that are related to the returned logs.
|
|
220
|
+
*/
|
|
221
|
+
transactions?: array<transactionSelection>,
|
|
222
|
+
/**
|
|
223
|
+
* List of trace selections, the query will return traces that match any of these selections and
|
|
224
|
+
* it will re turn traces that are related to the returned logs.
|
|
225
|
+
*/
|
|
226
|
+
traces?: array<traceSelection>,
|
|
227
|
+
/** List of block selections, the query will return blocks that match any of these selections */
|
|
228
|
+
blocks?: array<blockSelection>,
|
|
229
|
+
/**
|
|
230
|
+
* Field selection. The user can select which fields they are interested in, requesting less fields will improve
|
|
231
|
+
* query execution time and reduce the payload size so the user should always use a minimal number of fields.
|
|
232
|
+
*/
|
|
233
|
+
fieldSelection: fieldSelection,
|
|
234
|
+
/**
|
|
235
|
+
* Maximum number of blocks that should be returned, the server might return more blocks than this number but
|
|
236
|
+
* it won't overshoot by too much.
|
|
237
|
+
*/
|
|
238
|
+
maxNumBlocks?: int,
|
|
239
|
+
/**
|
|
240
|
+
* Maximum number of transactions that should be returned, the server might return more transactions than this number but
|
|
241
|
+
* it won't overshoot by too much.
|
|
242
|
+
*/
|
|
243
|
+
maxNumTransactions?: int,
|
|
244
|
+
/**
|
|
245
|
+
* Maximum number of logs that should be returned, the server might return more logs than this number but
|
|
246
|
+
* it won't overshoot by too much.
|
|
247
|
+
*/
|
|
248
|
+
maxNumLogs?: int,
|
|
249
|
+
/**
|
|
250
|
+
* Maximum number of traces that should be returned, the server might return more traces than this number but
|
|
251
|
+
* it won't overshoot by too much.
|
|
252
|
+
*/
|
|
253
|
+
maxNumTraces?: int,
|
|
254
|
+
/**
|
|
255
|
+
* Selects join mode for the query,
|
|
256
|
+
* Default: join in this order logs -> transactions -> traces -> blocks
|
|
257
|
+
* JoinAll: join everything to everything. For example if logSelection matches log0, we get the
|
|
258
|
+
* associated transaction of log0 and then we get associated logs of that transaction as well. Applites similarly
|
|
259
|
+
* to blocks, traces.
|
|
260
|
+
* JoinNothing: join nothing.
|
|
261
|
+
*/
|
|
262
|
+
joinMode?: joinMode,
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
module ResponseTypes = {
|
|
267
|
+
type withdrawal = {
|
|
268
|
+
index?: string,
|
|
269
|
+
validatorIndex?: string,
|
|
270
|
+
address?: Address.t,
|
|
271
|
+
amount?: string,
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
type block = {
|
|
275
|
+
number?: int,
|
|
276
|
+
hash?: string,
|
|
277
|
+
parentHash?: string,
|
|
278
|
+
nonce?: bigint,
|
|
279
|
+
sha3Uncles?: string,
|
|
280
|
+
logsBloom?: string,
|
|
281
|
+
transactionsRoot?: string,
|
|
282
|
+
stateRoot?: string,
|
|
283
|
+
receiptsRoot?: string,
|
|
284
|
+
miner?: Address.t,
|
|
285
|
+
difficulty?: bigint,
|
|
286
|
+
totalDifficulty?: bigint,
|
|
287
|
+
extraData?: string,
|
|
288
|
+
size?: bigint,
|
|
289
|
+
gasLimit?: bigint,
|
|
290
|
+
gasUsed?: bigint,
|
|
291
|
+
timestamp?: int,
|
|
292
|
+
uncles?: array<string>,
|
|
293
|
+
baseFeePerGas?: bigint,
|
|
294
|
+
blobGasUsed?: bigint,
|
|
295
|
+
excessBlobGas?: bigint,
|
|
296
|
+
parentBeaconBlockRoot?: string,
|
|
297
|
+
withdrawalsRoot?: string,
|
|
298
|
+
withdrawals?: array<withdrawal>,
|
|
299
|
+
l1BlockNumber?: int,
|
|
300
|
+
sendCount?: string,
|
|
301
|
+
sendRoot?: string,
|
|
302
|
+
mixHash?: string,
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
type accessList = {
|
|
306
|
+
address?: Address.t,
|
|
307
|
+
storageKeys?: array<string>,
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
type transaction = {
|
|
311
|
+
blockHash?: string,
|
|
312
|
+
blockNumber?: int,
|
|
313
|
+
from?: string,
|
|
314
|
+
gas?: bigint,
|
|
315
|
+
gasPrice?: bigint,
|
|
316
|
+
hash?: string,
|
|
317
|
+
input?: string,
|
|
318
|
+
nonce?: bigint,
|
|
319
|
+
to?: string,
|
|
320
|
+
transactionIndex?: int,
|
|
321
|
+
value?: bigint,
|
|
322
|
+
v?: string,
|
|
323
|
+
r?: string,
|
|
324
|
+
s?: string,
|
|
325
|
+
yParity?: string,
|
|
326
|
+
maxPriorityFeePerGas?: bigint,
|
|
327
|
+
maxFeePerGas?: bigint,
|
|
328
|
+
chainId?: int,
|
|
329
|
+
accessList?: array<accessList>,
|
|
330
|
+
maxFeePerBlobGas?: bigint,
|
|
331
|
+
blobVersionedHashes?: array<string>,
|
|
332
|
+
cumulativeGasUsed?: bigint,
|
|
333
|
+
effectiveGasPrice?: bigint,
|
|
334
|
+
gasUsed?: bigint,
|
|
335
|
+
contractAddress?: string,
|
|
336
|
+
logsBloom?: string,
|
|
337
|
+
kind?: int,
|
|
338
|
+
root?: string,
|
|
339
|
+
status?: int,
|
|
340
|
+
l1Fee?: bigint,
|
|
341
|
+
l1GasPrice?: bigint,
|
|
342
|
+
l1GasUsed?: bigint,
|
|
343
|
+
l1FeeScalar?: int,
|
|
344
|
+
gasUsedForL1?: bigint,
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
type log = {
|
|
348
|
+
removed?: bool,
|
|
349
|
+
@as("logIndex") index?: int,
|
|
350
|
+
transactionIndex?: int,
|
|
351
|
+
transactionHash?: string,
|
|
352
|
+
blockHash?: string,
|
|
353
|
+
blockNumber?: int,
|
|
354
|
+
address?: Address.t,
|
|
355
|
+
data?: string,
|
|
356
|
+
topics?: array<Js.Nullable.t<EvmTypes.Hex.t>>,
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
type event = {
|
|
360
|
+
transaction?: transaction,
|
|
361
|
+
block?: block,
|
|
362
|
+
log: log,
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
type rollbackGuard = {
|
|
366
|
+
/** Block number of the last scanned block */
|
|
367
|
+
blockNumber: int,
|
|
368
|
+
/** Block timestamp of the last scanned block */
|
|
369
|
+
timestamp: int,
|
|
370
|
+
/** Block hash of the last scanned block */
|
|
371
|
+
hash: string,
|
|
372
|
+
/**
|
|
373
|
+
* Block number of the first scanned block in memory.
|
|
374
|
+
*
|
|
375
|
+
* This might not be the first scanned block. It only includes blocks that are in memory (possible to be rolled back).
|
|
376
|
+
*/
|
|
377
|
+
firstBlockNumber: int,
|
|
378
|
+
/**
|
|
379
|
+
* Parent hash of the first scanned block in memory.
|
|
380
|
+
*
|
|
381
|
+
* This might not be the first scanned block. It only includes blocks that are in memory (possible to be rolled back).
|
|
382
|
+
*/
|
|
383
|
+
firstParentHash: string,
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
type eventResponse = {
|
|
387
|
+
/** Current height of the source hypersync instance */
|
|
388
|
+
archiveHeight: option<int>,
|
|
389
|
+
/**
|
|
390
|
+
* Next block to query for, the responses are paginated so,
|
|
391
|
+
* the caller should continue the query from this block if they
|
|
392
|
+
* didn't get responses up to the to_block they specified in the Query.
|
|
393
|
+
*/
|
|
394
|
+
nextBlock: int,
|
|
395
|
+
/** Total time it took the hypersync instance to execute the query. */
|
|
396
|
+
totalExecutionTime: int,
|
|
397
|
+
/** Response data */
|
|
398
|
+
data: array<event>,
|
|
399
|
+
/** Rollback guard, supposed to be used to detect rollbacks */
|
|
400
|
+
rollbackGuard: option<rollbackGuard>,
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
type query = QueryTypes.query
|
|
405
|
+
type eventResponse = ResponseTypes.eventResponse
|
|
406
|
+
|
|
407
|
+
//Todo, add bindings for these types
|
|
408
|
+
type streamConfig
|
|
409
|
+
type queryResponse
|
|
410
|
+
type queryResponseStream
|
|
411
|
+
type eventStream
|
|
412
|
+
type t = {
|
|
413
|
+
getHeight: unit => promise<int>,
|
|
414
|
+
collect: (~query: query, ~config: streamConfig) => promise<queryResponse>,
|
|
415
|
+
collectEvents: (~query: query, ~config: streamConfig) => promise<eventResponse>,
|
|
416
|
+
collectParquet: (~path: string, ~query: query, ~config: streamConfig) => promise<unit>,
|
|
417
|
+
get: (~query: query) => promise<queryResponse>,
|
|
418
|
+
getEvents: (~query: query) => promise<eventResponse>,
|
|
419
|
+
stream: (~query: query, ~config: streamConfig) => promise<queryResponseStream>,
|
|
420
|
+
streamEvents: (~query: query, ~config: streamConfig) => promise<eventStream>,
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
@module("@envio-dev/hypersync-client") @scope("HypersyncClient") external new: cfg => t = "new"
|
|
424
|
+
|
|
425
|
+
let defaultToken = "3dc856dd-b0ea-494f-b27e-017b8b6b7e07"
|
|
426
|
+
|
|
427
|
+
let make = (~url, ~bearerToken: option<string>, ~httpReqTimeoutMillis, ~maxNumRetries) =>
|
|
428
|
+
new({
|
|
429
|
+
url,
|
|
430
|
+
enableChecksumAddresses: true,
|
|
431
|
+
bearerToken: bearerToken->Belt.Option.getWithDefault(defaultToken),
|
|
432
|
+
httpReqTimeoutMillis,
|
|
433
|
+
maxNumRetries,
|
|
434
|
+
})
|
|
435
|
+
|
|
436
|
+
module Decoder = {
|
|
437
|
+
type rec decodedSolType<'a> = {val: 'a}
|
|
438
|
+
|
|
439
|
+
@unboxed
|
|
440
|
+
type rec decodedRaw =
|
|
441
|
+
| DecodedBool(bool)
|
|
442
|
+
| DecodedStr(string)
|
|
443
|
+
| DecodedNum(bigint)
|
|
444
|
+
| DecodedVal(decodedSolType<decodedRaw>)
|
|
445
|
+
| DecodedArr(array<decodedRaw>)
|
|
446
|
+
|
|
447
|
+
@unboxed
|
|
448
|
+
type rec decodedUnderlying =
|
|
449
|
+
| Bool(bool)
|
|
450
|
+
| Str(string)
|
|
451
|
+
| Num(bigint)
|
|
452
|
+
| Arr(array<decodedUnderlying>)
|
|
453
|
+
|
|
454
|
+
let rec toUnderlying = (d: decodedRaw): decodedUnderlying => {
|
|
455
|
+
switch d {
|
|
456
|
+
| DecodedVal(v) => v.val->toUnderlying
|
|
457
|
+
| DecodedBool(v) => Bool(v)
|
|
458
|
+
| DecodedStr(v) => Str(v)
|
|
459
|
+
| DecodedNum(v) => Num(v)
|
|
460
|
+
| DecodedArr(v) => v->Belt.Array.map(toUnderlying)->Arr
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
type decodedEvent = {
|
|
465
|
+
indexed: array<decodedRaw>,
|
|
466
|
+
body: array<decodedRaw>,
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
type log
|
|
470
|
+
type t = {
|
|
471
|
+
enableChecksummedAddresses: unit => unit,
|
|
472
|
+
disableChecksummedAddresses: unit => unit,
|
|
473
|
+
decodeLogs: array<log> => promise<array<Js.Nullable.t<decodedEvent>>>,
|
|
474
|
+
decodeLogsSync: array<log> => array<Js.Nullable.t<decodedEvent>>,
|
|
475
|
+
decodeEvents: array<ResponseTypes.event> => promise<array<Js.Nullable.t<decodedEvent>>>,
|
|
476
|
+
decodeEventsSync: array<ResponseTypes.event> => array<Js.Nullable.t<decodedEvent>>,
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
@module("@envio-dev/hypersync-client") @scope("Decoder")
|
|
480
|
+
external fromSignatures: array<string> => t = "fromSignatures"
|
|
481
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
type logLevelBuiltin = [
|
|
2
|
+
| #trace
|
|
3
|
+
| #debug
|
|
4
|
+
| #info
|
|
5
|
+
| #warn
|
|
6
|
+
| #error
|
|
7
|
+
| #fatal
|
|
8
|
+
]
|
|
9
|
+
@genType
|
|
10
|
+
type logLevelUser = [
|
|
11
|
+
| // NOTE: pino does better when these are all lowercase - some parts of the code lower case logs.
|
|
12
|
+
#udebug
|
|
13
|
+
| #uinfo
|
|
14
|
+
| #uwarn
|
|
15
|
+
| #uerror
|
|
16
|
+
]
|
|
17
|
+
type logLevel = [logLevelBuiltin | logLevelUser]
|
|
18
|
+
|
|
19
|
+
type pinoMessageBlob
|
|
20
|
+
type pinoMessageBlobWithError
|
|
21
|
+
@genType
|
|
22
|
+
type t = {
|
|
23
|
+
trace: pinoMessageBlob => unit,
|
|
24
|
+
debug: pinoMessageBlob => unit,
|
|
25
|
+
info: pinoMessageBlob => unit,
|
|
26
|
+
warn: pinoMessageBlob => unit,
|
|
27
|
+
error: pinoMessageBlob => unit,
|
|
28
|
+
fatal: pinoMessageBlob => unit,
|
|
29
|
+
}
|
|
30
|
+
@send external errorExn: (t, pinoMessageBlobWithError) => unit = "error"
|
|
31
|
+
|
|
32
|
+
// Bind to the 'level' property getter
|
|
33
|
+
@get external getLevel: t => logLevel = "level"
|
|
34
|
+
|
|
35
|
+
@ocaml.doc(`Get the available logging levels`) @get
|
|
36
|
+
external levels: t => 'a = "levels"
|
|
37
|
+
|
|
38
|
+
// Bind to the 'level' property setter
|
|
39
|
+
@set external setLevel: (t, logLevel) => unit = "level"
|
|
40
|
+
|
|
41
|
+
@ocaml.doc(`Identity function to help co-erce any type to a pino log message`)
|
|
42
|
+
let createPinoMessage = (message): pinoMessageBlob => Utils.magic(message)
|
|
43
|
+
let createPinoMessageWithError = (message, err): pinoMessageBlobWithError => {
|
|
44
|
+
//See https://github.com/pinojs/pino-std-serializers for standard pino serializers
|
|
45
|
+
//for common objects. We have also defined the serializer in this format in the
|
|
46
|
+
// serializers type below: `type serializers = {err: Js.Json.t => Js.Json.t}`
|
|
47
|
+
Utils.magic({
|
|
48
|
+
"msg": message,
|
|
49
|
+
"err": err,
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module Transport = {
|
|
54
|
+
type t
|
|
55
|
+
type optionsObject
|
|
56
|
+
let makeTransportOptions: 'a => optionsObject = Utils.magic
|
|
57
|
+
|
|
58
|
+
// NOTE: this config is pretty polymorphic - so keeping this as all optional fields.
|
|
59
|
+
type rec transportTarget = {
|
|
60
|
+
target?: string,
|
|
61
|
+
targets?: array<transportTarget>,
|
|
62
|
+
options?: optionsObject,
|
|
63
|
+
levels?: dict<int>,
|
|
64
|
+
level?: logLevel,
|
|
65
|
+
}
|
|
66
|
+
@module("pino")
|
|
67
|
+
external make: transportTarget => t = "transport"
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@module external makeWithTransport: Transport.t => t = "pino"
|
|
71
|
+
|
|
72
|
+
type hooks = {logMethod: (array<string>, string, logLevel) => unit}
|
|
73
|
+
|
|
74
|
+
type formatters = {
|
|
75
|
+
level: (string, int) => Js.Json.t,
|
|
76
|
+
bindings: Js.Json.t => Js.Json.t,
|
|
77
|
+
log: Js.Json.t => Js.Json.t,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
type serializers = {err: Js.Json.t => Js.Json.t}
|
|
81
|
+
|
|
82
|
+
type options = {
|
|
83
|
+
name?: string,
|
|
84
|
+
level?: logLevel,
|
|
85
|
+
customLevels?: dict<int>,
|
|
86
|
+
useOnlyCustomLevels?: bool,
|
|
87
|
+
depthLimit?: int,
|
|
88
|
+
edgeLimit?: int,
|
|
89
|
+
mixin?: unit => Js.Json.t,
|
|
90
|
+
mixinMergeStrategy?: (Js.Json.t, Js.Json.t) => Js.Json.t,
|
|
91
|
+
redact?: array<string>,
|
|
92
|
+
hooks?: hooks,
|
|
93
|
+
formatters?: formatters,
|
|
94
|
+
serializers?: serializers,
|
|
95
|
+
msgPrefix?: string,
|
|
96
|
+
base?: Js.Json.t,
|
|
97
|
+
enabled?: bool,
|
|
98
|
+
crlf?: bool,
|
|
99
|
+
timestamp?: bool,
|
|
100
|
+
messageKey?: string,
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@module external make: options => t = "pino"
|
|
104
|
+
@module external makeWithOptionsAndTransport: (options, Transport.t) => t = "pino"
|
|
105
|
+
|
|
106
|
+
type childParams
|
|
107
|
+
let createChildParams: 'a => childParams = Utils.magic
|
|
108
|
+
@send external child: (t, childParams) => t = "child"
|
|
109
|
+
|
|
110
|
+
module ECS = {
|
|
111
|
+
@module
|
|
112
|
+
external make: 'a => options = "@elastic/ecs-pino-format"
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
Jank solution to make logs use console log wrather than stream.write so that ink
|
|
117
|
+
can render the logs statically.
|
|
118
|
+
*/
|
|
119
|
+
module MultiStreamLogger = {
|
|
120
|
+
type stream = {write: string => unit}
|
|
121
|
+
type multiStream = {stream: stream, level: logLevel}
|
|
122
|
+
type multiStreamRes
|
|
123
|
+
@module("pino") external multistream: array<multiStream> => multiStreamRes = "multistream"
|
|
124
|
+
|
|
125
|
+
@module external makeWithMultiStream: (options, multiStreamRes) => t = "pino"
|
|
126
|
+
|
|
127
|
+
type destinationOpts = {
|
|
128
|
+
dest: string, //file path
|
|
129
|
+
sync: bool,
|
|
130
|
+
mkdir: bool,
|
|
131
|
+
}
|
|
132
|
+
@module("pino") external destination: destinationOpts => stream = "destination"
|
|
133
|
+
|
|
134
|
+
type prettyFactoryOpts = {...options, customColors?: string}
|
|
135
|
+
@module("pino-pretty")
|
|
136
|
+
external prettyFactory: prettyFactoryOpts => string => string = "prettyFactory"
|
|
137
|
+
|
|
138
|
+
let makeFormatter = logLevels => {
|
|
139
|
+
prettyFactory({
|
|
140
|
+
customLevels: logLevels,
|
|
141
|
+
customColors: "fatal:bgRed,error:red,warn:yellow,info:green,udebug:bgBlue,uinfo:bgGreen,uwarn:bgYellow,uerror:bgRed,debug:blue,trace:gray",
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
let makeStreams = (~userLogLevel, ~formatter, ~logFile, ~defaultFileLogLevel) => {
|
|
146
|
+
let stream = {
|
|
147
|
+
stream: {write: v => formatter(v)->Js.log},
|
|
148
|
+
level: userLogLevel,
|
|
149
|
+
}
|
|
150
|
+
let maybeFileStream = logFile->Belt.Option.mapWithDefault([], dest => [
|
|
151
|
+
{
|
|
152
|
+
level: defaultFileLogLevel,
|
|
153
|
+
stream: destination({dest, sync: false, mkdir: true}),
|
|
154
|
+
},
|
|
155
|
+
])
|
|
156
|
+
[stream]->Belt.Array.concat(maybeFileStream)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
let make = (
|
|
160
|
+
~userLogLevel: logLevel,
|
|
161
|
+
~customLevels: dict<int>,
|
|
162
|
+
~logFile: option<string>,
|
|
163
|
+
~options: option<options>,
|
|
164
|
+
~defaultFileLogLevel,
|
|
165
|
+
) => {
|
|
166
|
+
let options = switch options {
|
|
167
|
+
| Some(opts) => {...opts, customLevels, level: userLogLevel}
|
|
168
|
+
| None => {customLevels, level: userLogLevel}
|
|
169
|
+
}
|
|
170
|
+
let formatter = makeFormatter(customLevels)
|
|
171
|
+
let ms = makeStreams(~userLogLevel, ~formatter, ~logFile, ~defaultFileLogLevel)->multistream
|
|
172
|
+
|
|
173
|
+
makeWithMultiStream(options, ms)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
type eventLog = {
|
|
2
|
+
abi: EvmTypes.Abi.t,
|
|
3
|
+
data: string,
|
|
4
|
+
topics: array<EvmTypes.Hex.t>,
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
type decodedEvent<'a> = {
|
|
8
|
+
eventName: string,
|
|
9
|
+
args: 'a,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@module("viem") external decodeEventLogOrThrow: eventLog => decodedEvent<'a> = "decodeEventLog"
|
|
13
|
+
|
|
14
|
+
type hex = EvmTypes.Hex.t
|
|
15
|
+
@module("viem") external toHex: 'a => hex = "toHex"
|
|
16
|
+
@module("viem") external keccak256: hex => hex = "keccak256"
|
|
17
|
+
@module("viem") external keccak256Bytes: bytes => hex = "keccak256"
|
|
18
|
+
@module("viem") external pad: hex => hex = "pad"
|
|
19
|
+
@module("viem")
|
|
20
|
+
external encodePacked: (~types: array<string>, ~values: array<'a>) => hex = "encodePacked"
|
|
21
|
+
|
|
22
|
+
type sizeOptions = {size: int}
|
|
23
|
+
@module("viem") external intToHex: (int, ~options: sizeOptions=?) => hex = "numberToHex"
|
|
24
|
+
@module("viem") external bigintToHex: (bigint, ~options: sizeOptions=?) => hex = "numberToHex"
|
|
25
|
+
@module("viem") external stringToHex: (string, ~options: sizeOptions=?) => hex = "stringToHex"
|
|
26
|
+
@module("viem") external boolToHex: (bool, ~options: sizeOptions=?) => hex = "boolToHex"
|
|
27
|
+
@module("viem") external bytesToHex: (bytes, ~options: sizeOptions=?) => hex = "bytesToHex"
|
|
28
|
+
@module("viem") external concat: array<hex> => hex = "concat"
|