envio 2.17.0 → 2.18.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/package.json +6 -6
- package/src/ContractAddressingMap.res +115 -0
- package/src/ErrorHandling.res +61 -0
- package/src/EventUtils.res +87 -0
- package/src/Internal.res +2 -0
- package/src/LoadManager.res +174 -0
- package/src/Logging.res +179 -0
- package/src/Prometheus.res +316 -0
- package/src/Time.res +41 -0
- package/src/Utils.res +15 -0
- package/src/bindings/BigDecimal.gen.ts +8 -0
- package/src/bindings/BigDecimal.res +60 -0
- package/src/bindings/PromClient.res +58 -0
- package/src/db/EntityHistory.res +4 -4
- package/src/db/Table.res +9 -3
- package/src/sources/Fuel.res +37 -0
- package/src/sources/HyperFuel.res +260 -0
- package/src/sources/HyperFuel.resi +59 -0
- package/src/sources/HyperFuelClient.res +408 -0
- package/src/sources/HyperSync.res +349 -0
- package/src/sources/HyperSync.resi +69 -0
- package/src/sources/vendored-fuel-abi-coder.js +1847 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
let loadEntitiesDurationCounter = PromClient.Counter.makeCounter({
|
|
2
|
+
"name": "load_entities_processing_time_spent",
|
|
3
|
+
"help": "Duration spend on loading entities",
|
|
4
|
+
"labelNames": [],
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
let eventRouterDurationCounter = PromClient.Counter.makeCounter({
|
|
8
|
+
"name": "event_router_processing_time_spent",
|
|
9
|
+
"help": "Duration spend on event routing",
|
|
10
|
+
"labelNames": [],
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
let executeBatchDurationCounter = PromClient.Counter.makeCounter({
|
|
14
|
+
"name": "execute_batch_processing_time_spent",
|
|
15
|
+
"help": "Duration spend on executing batch",
|
|
16
|
+
"labelNames": [],
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
let eventsProcessedCounter = PromClient.Gauge.makeGauge({
|
|
20
|
+
"name": "events_processed",
|
|
21
|
+
"help": "Total number of events processed",
|
|
22
|
+
"labelNames": ["chainId"],
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
let reorgsDetectedCounter = PromClient.Counter.makeCounter({
|
|
26
|
+
"name": "reorgs_detected",
|
|
27
|
+
"help": "Total number of reorgs detected",
|
|
28
|
+
"labelNames": ["chainId"],
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
let allChainsSyncedToHead = PromClient.Gauge.makeGauge({
|
|
32
|
+
"name": "hyperindex_synced_to_head",
|
|
33
|
+
"help": "All chains fully synced",
|
|
34
|
+
"labelNames": [],
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
let sourceChainHeight = PromClient.Gauge.makeGauge({
|
|
38
|
+
"name": "chain_block_height",
|
|
39
|
+
"help": "Chain Height of Source Chain",
|
|
40
|
+
"labelNames": ["chainId"],
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
module Labels = {
|
|
44
|
+
let rec schemaIsString = (schema: S.t<'a>) =>
|
|
45
|
+
switch schema->S.classify {
|
|
46
|
+
| String => true
|
|
47
|
+
| Null(s)
|
|
48
|
+
| Option(s) =>
|
|
49
|
+
s->schemaIsString
|
|
50
|
+
| _ => false
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let getLabelNames = (schema: S.t<'a>) =>
|
|
54
|
+
switch schema->S.classify {
|
|
55
|
+
| Object({items}) =>
|
|
56
|
+
let nonStringFields = items->Belt.Array.reduce([], (nonStringFields, item) => {
|
|
57
|
+
if item.schema->schemaIsString {
|
|
58
|
+
nonStringFields
|
|
59
|
+
} else {
|
|
60
|
+
nonStringFields->Belt.Array.concat([item.location])
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
switch nonStringFields {
|
|
65
|
+
| [] => items->Belt.Array.map(item => item.location)->Ok
|
|
66
|
+
| nonStringItems =>
|
|
67
|
+
let nonStringItems = nonStringItems->Js.Array2.joinWith(", ")
|
|
68
|
+
Error(
|
|
69
|
+
`Label schema must be an object with string (or optional string) values. Non string values: ${nonStringItems}`,
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
| _ => Error("Label schema must be an object")
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let metricNames: Utils.Set.t<string> = Utils.Set.make()
|
|
77
|
+
|
|
78
|
+
module MakeSafePromMetric = (
|
|
79
|
+
M: {
|
|
80
|
+
type t
|
|
81
|
+
let make: {"name": string, "help": string, "labelNames": array<string>} => t
|
|
82
|
+
let labels: (t, 'a) => t
|
|
83
|
+
let handleFloat: (t, float) => unit
|
|
84
|
+
let handleInt: (t, int) => unit
|
|
85
|
+
},
|
|
86
|
+
): {
|
|
87
|
+
type t<'a>
|
|
88
|
+
let makeOrThrow: (~name: string, ~help: string, ~labelSchema: S.t<'a>) => t<'a>
|
|
89
|
+
let handleInt: (t<'a>, ~labels: 'a, ~value: int) => unit
|
|
90
|
+
let handleFloat: (t<'a>, ~labels: 'a, ~value: float) => unit
|
|
91
|
+
} => {
|
|
92
|
+
type t<'a> = {metric: M.t, labelSchema: S.t<'a>}
|
|
93
|
+
|
|
94
|
+
let makeOrThrow = (~name, ~help, ~labelSchema: S.t<'a>): t<'a> =>
|
|
95
|
+
switch labelSchema->Labels.getLabelNames {
|
|
96
|
+
| Ok(labelNames) =>
|
|
97
|
+
if metricNames->Utils.Set.has(name) {
|
|
98
|
+
Js.Exn.raiseError("Duplicate prometheus metric name: " ++ name)
|
|
99
|
+
} else {
|
|
100
|
+
metricNames->Utils.Set.add(name)->ignore
|
|
101
|
+
let metric = M.make({
|
|
102
|
+
"name": name,
|
|
103
|
+
"help": help,
|
|
104
|
+
"labelNames": labelNames,
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
{metric, labelSchema}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
| Error(error) => Js.Exn.raiseError(error)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
let handleFloat = ({metric, labelSchema}: t<'a>, ~labels: 'a, ~value) =>
|
|
114
|
+
metric
|
|
115
|
+
->M.labels(labels->S.reverseConvertToJsonOrThrow(labelSchema))
|
|
116
|
+
->M.handleFloat(value)
|
|
117
|
+
|
|
118
|
+
let handleInt = ({metric, labelSchema}: t<'a>, ~labels: 'a, ~value) =>
|
|
119
|
+
metric
|
|
120
|
+
->M.labels(labels->S.reverseConvertToJsonOrThrow(labelSchema))
|
|
121
|
+
->M.handleInt(value)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
module SafeCounter = MakeSafePromMetric({
|
|
125
|
+
type t = PromClient.Counter.counter
|
|
126
|
+
let make = PromClient.Counter.makeCounter
|
|
127
|
+
let labels = PromClient.Counter.labels
|
|
128
|
+
let handleInt = PromClient.Counter.incMany
|
|
129
|
+
let handleFloat = PromClient.Counter.incMany->Utils.magic
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
module SafeGauge = MakeSafePromMetric({
|
|
133
|
+
type t = PromClient.Gauge.gauge
|
|
134
|
+
let make = PromClient.Gauge.makeGauge
|
|
135
|
+
let labels = PromClient.Gauge.labels
|
|
136
|
+
let handleInt = PromClient.Gauge.set
|
|
137
|
+
let handleFloat = PromClient.Gauge.setFloat
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
module BenchmarkSummaryData = {
|
|
141
|
+
type labels = {
|
|
142
|
+
group: string,
|
|
143
|
+
stat: string,
|
|
144
|
+
label: string,
|
|
145
|
+
}
|
|
146
|
+
let labelSchema = S.schema(s => {
|
|
147
|
+
group: s.matches(S.string),
|
|
148
|
+
stat: s.matches(S.string),
|
|
149
|
+
label: s.matches(S.string),
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
let gauge = SafeGauge.makeOrThrow(
|
|
153
|
+
~name="benchmark_summary_data",
|
|
154
|
+
~help="All data points collected during indexer benchmark",
|
|
155
|
+
~labelSchema,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
let set = (
|
|
159
|
+
~group: string,
|
|
160
|
+
~label: string,
|
|
161
|
+
~n: float,
|
|
162
|
+
~mean: float,
|
|
163
|
+
~stdDev: option<float>,
|
|
164
|
+
~min: float,
|
|
165
|
+
~max: float,
|
|
166
|
+
~sum: float,
|
|
167
|
+
) => {
|
|
168
|
+
let mk = stat => {
|
|
169
|
+
group,
|
|
170
|
+
stat,
|
|
171
|
+
label,
|
|
172
|
+
}
|
|
173
|
+
gauge->SafeGauge.handleFloat(~labels=mk("n"), ~value=n)
|
|
174
|
+
gauge->SafeGauge.handleFloat(~labels=mk("mean"), ~value=mean)
|
|
175
|
+
gauge->SafeGauge.handleFloat(~labels=mk("min"), ~value=min)
|
|
176
|
+
gauge->SafeGauge.handleFloat(~labels=mk("max"), ~value=max)
|
|
177
|
+
gauge->SafeGauge.handleFloat(~labels=mk("sum"), ~value=sum)
|
|
178
|
+
switch stdDev {
|
|
179
|
+
| Some(stdDev) => gauge->SafeGauge.handleFloat(~labels=mk("stdDev"), ~value=stdDev)
|
|
180
|
+
| None => ()
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
let processedUntilHeight = PromClient.Gauge.makeGauge({
|
|
186
|
+
"name": "chain_block_height_processed",
|
|
187
|
+
"help": "Block height processed by indexer",
|
|
188
|
+
"labelNames": ["chainId"],
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
let fetchedUntilHeight = PromClient.Gauge.makeGauge({
|
|
192
|
+
"name": "chain_block_height_fully_fetched",
|
|
193
|
+
"help": "Block height fully fetched by indexer",
|
|
194
|
+
"labelNames": ["chainId"],
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
let incrementLoadEntityDurationCounter = (~duration) => {
|
|
198
|
+
loadEntitiesDurationCounter->PromClient.Counter.incMany(duration)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
let incrementEventRouterDurationCounter = (~duration) => {
|
|
202
|
+
eventRouterDurationCounter->PromClient.Counter.incMany(duration)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
let incrementExecuteBatchDurationCounter = (~duration) => {
|
|
206
|
+
executeBatchDurationCounter->PromClient.Counter.incMany(duration)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
let setEventsProcessedGuage = (~number, ~chainId) => {
|
|
210
|
+
eventsProcessedCounter
|
|
211
|
+
->PromClient.Gauge.labels({"chainId": chainId})
|
|
212
|
+
->PromClient.Gauge.set(number)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
let incrementReorgsDetected = (~chain) => {
|
|
216
|
+
reorgsDetectedCounter->PromClient.Counter.incLabels({"chainId": chain->ChainMap.Chain.toString})
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
let setSourceChainHeight = (~blockNumber, ~chain) => {
|
|
220
|
+
sourceChainHeight
|
|
221
|
+
->PromClient.Gauge.labels({"chainId": chain->ChainMap.Chain.toString})
|
|
222
|
+
->PromClient.Gauge.set(blockNumber)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
let setAllChainsSyncedToHead = () => {
|
|
226
|
+
allChainsSyncedToHead->PromClient.Gauge.set(1)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
let setProcessedUntilHeight = (~blockNumber, ~chain) => {
|
|
230
|
+
processedUntilHeight
|
|
231
|
+
->PromClient.Gauge.labels({"chainId": chain->ChainMap.Chain.toString})
|
|
232
|
+
->PromClient.Gauge.set(blockNumber)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
let setFetchedUntilHeight = (~blockNumber, ~chain) => {
|
|
236
|
+
fetchedUntilHeight
|
|
237
|
+
->PromClient.Gauge.labels({"chainId": chain->ChainMap.Chain.toString})
|
|
238
|
+
->PromClient.Gauge.set(blockNumber)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
module BenchmarkCounters = {
|
|
242
|
+
type labels = {label: string}
|
|
243
|
+
let labelSchema = S.schema(s => {
|
|
244
|
+
label: s.matches(S.string),
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
let gauge = SafeGauge.makeOrThrow(
|
|
248
|
+
~name="benchmark_counters",
|
|
249
|
+
~help="All counters collected during indexer benchmark",
|
|
250
|
+
~labelSchema,
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
let set = (~label, ~millis, ~totalRuntimeMillis) => {
|
|
254
|
+
gauge->SafeGauge.handleFloat(~labels={label: label}, ~value=millis)
|
|
255
|
+
gauge->SafeGauge.handleFloat(~labels={label: "Total Run Time (ms)"}, ~value=totalRuntimeMillis)
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
module PartitionBlockFetched = {
|
|
260
|
+
type labels = {chainId: int, partitionId: string}
|
|
261
|
+
|
|
262
|
+
let labelSchema = S.schema(s => {
|
|
263
|
+
chainId: s.matches(S.string->S.coerce(S.int)),
|
|
264
|
+
partitionId: s.matches(S.string),
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
let counter = SafeGauge.makeOrThrow(
|
|
268
|
+
~name="partition_block_fetched",
|
|
269
|
+
~help="The latest fetched block number for each partition",
|
|
270
|
+
~labelSchema,
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
let set = (~blockNumber, ~partitionId, ~chainId) => {
|
|
274
|
+
counter->SafeGauge.handleInt(~labels={chainId, partitionId}, ~value=blockNumber)
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
let chainIdLabelsSchema = S.object(s => {
|
|
279
|
+
s.field("chainId", S.string->S.coerce(S.int))
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
module IndexingAddresses = {
|
|
283
|
+
let gauge = SafeGauge.makeOrThrow(
|
|
284
|
+
~name="envio_indexing_addresses",
|
|
285
|
+
~help="The number of addresses indexed on chain. Includes both static and dynamic addresses.",
|
|
286
|
+
~labelSchema=chainIdLabelsSchema,
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
let set = (~addressesCount, ~chainId) => {
|
|
290
|
+
gauge->SafeGauge.handleInt(~labels=chainId, ~value=addressesCount)
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
module IndexingEndBlock = {
|
|
295
|
+
let gauge = SafeGauge.makeOrThrow(
|
|
296
|
+
~name="envio_indexing_end_block",
|
|
297
|
+
~help="The block number to stop indexing at. (inclusive)",
|
|
298
|
+
~labelSchema=chainIdLabelsSchema,
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
let set = (~endBlock, ~chainId) => {
|
|
302
|
+
gauge->SafeGauge.handleInt(~labels=chainId, ~value=endBlock)
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
module ProgressBlockNumber = {
|
|
307
|
+
let gauge = SafeGauge.makeOrThrow(
|
|
308
|
+
~name="envio_progress_block_number",
|
|
309
|
+
~help="The block number to track the progress of indexing at. Currently uses the fully fetched block number. In the future will be changed to block number processed and stored in the database.",
|
|
310
|
+
~labelSchema=chainIdLabelsSchema,
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
let set = (~endBlock, ~chainId) => {
|
|
314
|
+
gauge->SafeGauge.handleInt(~labels=chainId, ~value=endBlock)
|
|
315
|
+
}
|
|
316
|
+
}
|
package/src/Time.res
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
let resolvePromiseAfterDelay = (~delayMilliseconds) => Utils.delay(delayMilliseconds)
|
|
2
|
+
|
|
3
|
+
let rec retryAsyncWithExponentialBackOff = async (
|
|
4
|
+
~backOffMillis=100,
|
|
5
|
+
~multiplicative=4,
|
|
6
|
+
~retryCount=0,
|
|
7
|
+
~maxRetries=5,
|
|
8
|
+
~logger: Pino.t,
|
|
9
|
+
f: unit => promise<'a>,
|
|
10
|
+
) => {
|
|
11
|
+
try {
|
|
12
|
+
await f()
|
|
13
|
+
} catch {
|
|
14
|
+
| exn =>
|
|
15
|
+
if retryCount < maxRetries {
|
|
16
|
+
let nextRetryCount = retryCount + 1
|
|
17
|
+
let log = retryCount === 0 ? Logging.childTrace : Logging.childWarn
|
|
18
|
+
logger->log({
|
|
19
|
+
"msg": `Retrying query ${nextRetryCount->Belt.Int.toString}/${maxRetries->Belt.Int.toString} in ${backOffMillis->Belt.Int.toString}ms - waiting for correct result.`,
|
|
20
|
+
"err": exn->Internal.prettifyExn,
|
|
21
|
+
})
|
|
22
|
+
await resolvePromiseAfterDelay(~delayMilliseconds=backOffMillis)
|
|
23
|
+
|
|
24
|
+
await f->retryAsyncWithExponentialBackOff(
|
|
25
|
+
~backOffMillis=backOffMillis * multiplicative,
|
|
26
|
+
~multiplicative,
|
|
27
|
+
~retryCount=nextRetryCount,
|
|
28
|
+
~maxRetries,
|
|
29
|
+
~logger,
|
|
30
|
+
)
|
|
31
|
+
} else {
|
|
32
|
+
exn
|
|
33
|
+
->ErrorHandling.make(
|
|
34
|
+
~logger,
|
|
35
|
+
~msg=`Failure. Max retries ${retryCount->Belt.Int.toString}/${maxRetries->Belt.Int.toString} exceeded`,
|
|
36
|
+
)
|
|
37
|
+
->ErrorHandling.log
|
|
38
|
+
await Promise.reject(exn->Js.Exn.anyToExnInternal)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/Utils.res
CHANGED
|
@@ -22,6 +22,11 @@ module Object = {
|
|
|
22
22
|
external defineProperty: ('obj, string, propertyDescriptor<'a>) => 'obj = "defineProperty"
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
module Error = {
|
|
26
|
+
@new
|
|
27
|
+
external make: string => exn = "Error"
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
module Option = {
|
|
26
31
|
let mapNone = (opt: option<'a>, val: 'b): option<'b> => {
|
|
27
32
|
switch opt {
|
|
@@ -123,6 +128,9 @@ module Math = {
|
|
|
123
128
|
}
|
|
124
129
|
|
|
125
130
|
module Array = {
|
|
131
|
+
@send
|
|
132
|
+
external forEachAsync: (array<'a>, 'a => promise<unit>) => unit = "forEach"
|
|
133
|
+
|
|
126
134
|
@val external jsArrayCreate: int => array<'a> = "Array"
|
|
127
135
|
|
|
128
136
|
/* Given a comaprator and two sorted lists, combine them into a single sorted list */
|
|
@@ -452,3 +460,10 @@ module Map = {
|
|
|
452
460
|
@send external set: (t<'k, 'v>, 'k, 'v) => t<'k, 'v> = "set"
|
|
453
461
|
@send external delete: (t<'k, 'v>, 'k) => bool = "delete"
|
|
454
462
|
}
|
|
463
|
+
|
|
464
|
+
module Proxy = {
|
|
465
|
+
type traps<'a> = {get?: (~target: 'a, ~prop: unknown) => unknown}
|
|
466
|
+
|
|
467
|
+
@new
|
|
468
|
+
external make: ('a, traps<'a>) => 'a = "Proxy"
|
|
469
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
@genType.import(("bignumber.js", "default"))
|
|
2
|
+
type rec t = {
|
|
3
|
+
toString: unit => string,
|
|
4
|
+
toFixed: int => string,
|
|
5
|
+
plus: t => t,
|
|
6
|
+
minus: t => t,
|
|
7
|
+
times: t => t,
|
|
8
|
+
div: t => t,
|
|
9
|
+
isEqualTo: t => bool,
|
|
10
|
+
gt: t => bool,
|
|
11
|
+
gte: t => bool,
|
|
12
|
+
lt: t => bool,
|
|
13
|
+
lte: t => bool,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Constructors
|
|
17
|
+
@new @module external fromBigInt: bigint => t = "bignumber.js"
|
|
18
|
+
@new @module external fromFloat: float => t = "bignumber.js"
|
|
19
|
+
@new @module external fromInt: int => t = "bignumber.js"
|
|
20
|
+
@new @module external fromStringUnsafe: string => t = "bignumber.js"
|
|
21
|
+
@new @module external fromString: string => option<t> = "bignumber.js"
|
|
22
|
+
|
|
23
|
+
// Methods
|
|
24
|
+
@send external toString: t => string = "toString"
|
|
25
|
+
@send external toFixed: t => string = "toFixed"
|
|
26
|
+
let toInt = (b: t): option<int> => b->toString->Belt.Int.fromString
|
|
27
|
+
@send external toNumber: t => float = "toNumber"
|
|
28
|
+
|
|
29
|
+
// Arithmetic Operations
|
|
30
|
+
@send external plus: (t, t) => t = "plus"
|
|
31
|
+
@send external minus: (t, t) => t = "minus"
|
|
32
|
+
@send external times: (t, t) => t = "multipliedBy"
|
|
33
|
+
@send external div: (t, t) => t = "dividedBy"
|
|
34
|
+
@send external sqrt: t => t = "sqrt"
|
|
35
|
+
|
|
36
|
+
// Comparison
|
|
37
|
+
@send external equals: (t, t) => bool = "isEqualTo"
|
|
38
|
+
let notEquals: (t, t) => bool = (a, b) => !equals(a, b)
|
|
39
|
+
@send external gt: (t, t) => bool = "isGreaterThan"
|
|
40
|
+
@send external gte: (t, t) => bool = "isGreaterThanOrEqualTo"
|
|
41
|
+
@send external lt: (t, t) => bool = "isLessThan"
|
|
42
|
+
@send external lte: (t, t) => bool = "isLessThanOrEqualTo"
|
|
43
|
+
|
|
44
|
+
// Utilities
|
|
45
|
+
let zero = fromInt(0)
|
|
46
|
+
let one = fromInt(1)
|
|
47
|
+
@send external decimalPlaces: (t, int) => t = "decimalPlaces"
|
|
48
|
+
|
|
49
|
+
// Serialization
|
|
50
|
+
let schema =
|
|
51
|
+
S.string
|
|
52
|
+
->S.setName("BigDecimal")
|
|
53
|
+
->S.transform(s => {
|
|
54
|
+
parser: string =>
|
|
55
|
+
switch string->fromString {
|
|
56
|
+
| Some(bigDecimal) => bigDecimal
|
|
57
|
+
| None => s.fail("The string is not valid BigDecimal")
|
|
58
|
+
},
|
|
59
|
+
serializer: bigDecimal => bigDecimal.toString(),
|
|
60
|
+
})
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/** All metric types have two mandatory parameters: name and help. Refer to https://prometheus.io/docs/practices/naming/ for guidance on naming metrics. */
|
|
2
|
+
type customMetric<'a> = {.."name": string, "help": string} as 'a
|
|
3
|
+
|
|
4
|
+
@module("prom-client") external collectDefaultMetrics: 'a => unit = "collectDefaultMetrics"
|
|
5
|
+
|
|
6
|
+
type registry
|
|
7
|
+
@new @module("prom-client") external makeRegistry: unit => registry = "Registry"
|
|
8
|
+
|
|
9
|
+
@module("prom-client") external defaultRegister: registry = "register"
|
|
10
|
+
|
|
11
|
+
@send external metrics: registry => Promise.t<string> = "metrics"
|
|
12
|
+
@get external getContentType: registry => string = "contentType"
|
|
13
|
+
|
|
14
|
+
module Counter = {
|
|
15
|
+
type counter
|
|
16
|
+
@new @module("prom-client") external makeCounter: customMetric<'a> => counter = "Counter"
|
|
17
|
+
|
|
18
|
+
@send external inc: counter => unit = "inc"
|
|
19
|
+
@send external incMany: (counter, int) => unit = "inc"
|
|
20
|
+
|
|
21
|
+
@send external incLabels: (counter, 'labelsObject) => unit = "labels"
|
|
22
|
+
@send external labels: (counter, 'labelsObject) => counter = "labels"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module Gauge = {
|
|
26
|
+
type gauge
|
|
27
|
+
@new @module("prom-client") external makeGauge: customMetric<'a> => gauge = "Gauge"
|
|
28
|
+
|
|
29
|
+
@send external inc: gauge => unit = "inc"
|
|
30
|
+
@send external incMany: (gauge, int) => unit = "inc"
|
|
31
|
+
|
|
32
|
+
@send external dec: gauge => unit = "dec"
|
|
33
|
+
@send external decMany: (gauge, int) => unit = "dec"
|
|
34
|
+
|
|
35
|
+
@send external set: (gauge, int) => unit = "set"
|
|
36
|
+
|
|
37
|
+
@send external setFloat: (gauge, float) => unit = "set"
|
|
38
|
+
|
|
39
|
+
@send external labels: (gauge, 'labelsObject) => gauge = "labels"
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module Histogram = {
|
|
43
|
+
type histogram
|
|
44
|
+
@new @module("prom-client") external makeHistogram: customMetric<'a> => histogram = "Histogram"
|
|
45
|
+
|
|
46
|
+
@send external observe: (histogram, float) => unit = "observe"
|
|
47
|
+
@send external startTimer: (histogram, unit) => float = "startTimer"
|
|
48
|
+
|
|
49
|
+
@send external labels: (histogram, 'labelsObject) => histogram = "labels"
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module Summary = {
|
|
53
|
+
type summary
|
|
54
|
+
@new @module("prom-client") external makeSummary: customMetric<'a> => summary = "Summary"
|
|
55
|
+
|
|
56
|
+
@send external observe: (summary, float) => unit = "observe"
|
|
57
|
+
@send external startTimer: (summary, unit) => float = "startTimer"
|
|
58
|
+
}
|
package/src/db/EntityHistory.res
CHANGED
|
@@ -186,7 +186,7 @@ let fromTable = (table: table, ~schema: S.t<'entity>): t<'entity> => {
|
|
|
186
186
|
|
|
187
187
|
let currentHistoryFields =
|
|
188
188
|
currentChangeFieldNames->Belt.Array.map(fieldName =>
|
|
189
|
-
mkField(fieldName, Integer, ~isPrimaryKey=true)
|
|
189
|
+
mkField(fieldName, Integer, ~fieldSchema=S.never, ~isPrimaryKey=true)
|
|
190
190
|
)
|
|
191
191
|
|
|
192
192
|
let previousChangeFieldNames =
|
|
@@ -194,7 +194,7 @@ let fromTable = (table: table, ~schema: S.t<'entity>): t<'entity> => {
|
|
|
194
194
|
|
|
195
195
|
let previousHistoryFields =
|
|
196
196
|
previousChangeFieldNames->Belt.Array.map(fieldName =>
|
|
197
|
-
mkField(fieldName, Integer, ~isNullable=true)
|
|
197
|
+
mkField(fieldName, Integer, ~fieldSchema=S.never, ~isNullable=true)
|
|
198
198
|
)
|
|
199
199
|
|
|
200
200
|
let id = "id"
|
|
@@ -224,9 +224,9 @@ let fromTable = (table: table, ~schema: S.t<'entity>): t<'entity> => {
|
|
|
224
224
|
|
|
225
225
|
let actionFieldName = "action"
|
|
226
226
|
|
|
227
|
-
let actionField = mkField(actionFieldName, Custom(RowAction.enum.name))
|
|
227
|
+
let actionField = mkField(actionFieldName, Custom(RowAction.enum.name), ~fieldSchema=S.never)
|
|
228
228
|
|
|
229
|
-
let serialField = mkField("serial", Serial, ~isNullable=true, ~isIndex=true)
|
|
229
|
+
let serialField = mkField("serial", Serial, ~fieldSchema=S.never, ~isNullable=true, ~isIndex=true)
|
|
230
230
|
|
|
231
231
|
let dataFieldNames = dataFields->Belt.Array.map(field => field->getFieldName)
|
|
232
232
|
|
package/src/db/Table.res
CHANGED
|
@@ -19,6 +19,7 @@ type fieldType =
|
|
|
19
19
|
type field = {
|
|
20
20
|
fieldName: string,
|
|
21
21
|
fieldType: fieldType,
|
|
22
|
+
fieldSchema: S.t<unknown>,
|
|
22
23
|
isArray: bool,
|
|
23
24
|
isNullable: bool,
|
|
24
25
|
isPrimaryKey: bool,
|
|
@@ -36,18 +37,20 @@ type derivedFromField = {
|
|
|
36
37
|
type fieldOrDerived = Field(field) | DerivedFrom(derivedFromField)
|
|
37
38
|
|
|
38
39
|
let mkField = (
|
|
40
|
+
fieldName,
|
|
41
|
+
fieldType,
|
|
42
|
+
~fieldSchema,
|
|
39
43
|
~default=?,
|
|
40
44
|
~isArray=false,
|
|
41
45
|
~isNullable=false,
|
|
42
46
|
~isPrimaryKey=false,
|
|
43
47
|
~isIndex=false,
|
|
44
48
|
~linkedEntity=?,
|
|
45
|
-
fieldName,
|
|
46
|
-
fieldType,
|
|
47
49
|
) =>
|
|
48
50
|
{
|
|
49
51
|
fieldName,
|
|
50
52
|
fieldType,
|
|
53
|
+
fieldSchema: fieldSchema->S.toUnknown,
|
|
51
54
|
isArray,
|
|
52
55
|
isNullable,
|
|
53
56
|
isPrimaryKey,
|
|
@@ -211,7 +214,10 @@ let toSqlParams = (table: table, ~schema) => {
|
|
|
211
214
|
}
|
|
212
215
|
| Bool =>
|
|
213
216
|
// Workaround for https://github.com/porsager/postgres/issues/471
|
|
214
|
-
S.union([
|
|
217
|
+
S.union([
|
|
218
|
+
S.literal(1)->S.shape(_ => true),
|
|
219
|
+
S.literal(0)->S.shape(_ => false),
|
|
220
|
+
])->S.toUnknown
|
|
215
221
|
| _ => schema
|
|
216
222
|
}
|
|
217
223
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
type receiptType =
|
|
2
|
+
| @as(0) Call
|
|
3
|
+
| @as(1) Return
|
|
4
|
+
| @as(2) ReturnData
|
|
5
|
+
| @as(3) Panic
|
|
6
|
+
| @as(4) Revert
|
|
7
|
+
| @as(5) Log
|
|
8
|
+
| @as(6) LogData
|
|
9
|
+
// Transfer is to another contract, TransferOut is to wallet address
|
|
10
|
+
| @as(7) Transfer
|
|
11
|
+
| @as(8) TransferOut
|
|
12
|
+
| @as(9) ScriptResult
|
|
13
|
+
| @as(10) MessageOut
|
|
14
|
+
| @as(11) Mint
|
|
15
|
+
| @as(12) Burn
|
|
16
|
+
|
|
17
|
+
@module("./vendored-fuel-abi-coder.js")
|
|
18
|
+
external transpileAbi: Js.Json.t => Ethers.abi = "transpileAbi"
|
|
19
|
+
|
|
20
|
+
@module("./vendored-fuel-abi-coder.js") @scope("AbiCoder")
|
|
21
|
+
external getLogDecoder: (~abi: Ethers.abi, ~logId: string) => string => unknown = "getLogDecoder"
|
|
22
|
+
|
|
23
|
+
module Receipt = {
|
|
24
|
+
@tag("receiptType")
|
|
25
|
+
type t =
|
|
26
|
+
| @as(0) Call({assetId: string, amount: bigint, to: string})
|
|
27
|
+
| @as(6) LogData({data: string, rb: bigint})
|
|
28
|
+
| @as(7) Transfer({amount: bigint, assetId: string, to: string})
|
|
29
|
+
| @as(8) TransferOut({amount: bigint, assetId: string, toAddress: string})
|
|
30
|
+
| @as(11) Mint({val: bigint, subId: string})
|
|
31
|
+
| @as(12) Burn({val: bigint, subId: string})
|
|
32
|
+
|
|
33
|
+
let getLogDataDecoder = (~abi: Ethers.abi, ~logId: string) => {
|
|
34
|
+
let decode = getLogDecoder(~abi, ~logId)
|
|
35
|
+
data => data->decode->Utils.magic
|
|
36
|
+
}
|
|
37
|
+
}
|