envio 3.0.0-alpha.2 → 3.0.0-alpha.21
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 +164 -30
- package/bin.mjs +49 -0
- package/evm.schema.json +79 -169
- package/fuel.schema.json +50 -21
- package/index.d.ts +578 -1
- package/index.js +4 -0
- package/package.json +47 -31
- package/rescript.json +4 -1
- package/src/Batch.res +11 -8
- package/src/Batch.res.mjs +11 -9
- package/src/ChainFetcher.res +531 -0
- package/src/ChainFetcher.res.mjs +339 -0
- package/src/ChainManager.res +190 -0
- package/src/ChainManager.res.mjs +166 -0
- package/src/Change.res +3 -3
- package/src/Config.gen.ts +19 -0
- package/src/Config.res +725 -25
- package/src/Config.res.mjs +692 -26
- package/src/{Indexer.res → Ctx.res} +1 -1
- package/src/Ecosystem.res +9 -124
- package/src/Ecosystem.res.mjs +19 -160
- package/src/Env.res +33 -73
- package/src/Env.res.mjs +29 -85
- package/src/Envio.gen.ts +3 -1
- package/src/Envio.res +77 -9
- package/src/Envio.res.mjs +39 -1
- package/src/EventConfigBuilder.res +408 -0
- package/src/EventConfigBuilder.res.mjs +376 -0
- package/src/EventProcessing.res +469 -0
- package/src/EventProcessing.res.mjs +337 -0
- package/src/EvmTypes.gen.ts +6 -0
- package/src/EvmTypes.res +1 -0
- package/src/FetchState.res +1256 -639
- package/src/FetchState.res.mjs +1135 -612
- package/src/GlobalState.res +1224 -0
- package/src/GlobalState.res.mjs +1291 -0
- package/src/GlobalStateManager.res +68 -0
- package/src/GlobalStateManager.res.mjs +75 -0
- package/src/GlobalStateManager.resi +7 -0
- package/src/HandlerLoader.res +89 -0
- package/src/HandlerLoader.res.mjs +79 -0
- package/src/HandlerRegister.res +357 -0
- package/src/HandlerRegister.res.mjs +299 -0
- package/src/HandlerRegister.resi +30 -0
- package/src/Hasura.res +111 -175
- package/src/Hasura.res.mjs +88 -150
- package/src/InMemoryStore.res +1 -1
- package/src/InMemoryStore.res.mjs +3 -3
- package/src/InMemoryTable.res +1 -1
- package/src/InMemoryTable.res.mjs +1 -1
- package/src/Internal.gen.ts +6 -0
- package/src/Internal.res +265 -12
- package/src/Internal.res.mjs +115 -1
- package/src/LoadLayer.res +444 -0
- package/src/LoadLayer.res.mjs +296 -0
- package/src/LoadLayer.resi +32 -0
- package/src/LogSelection.res +33 -27
- package/src/LogSelection.res.mjs +6 -0
- package/src/Logging.res +21 -7
- package/src/Logging.res.mjs +16 -8
- package/src/Main.res +390 -0
- package/src/Main.res.mjs +341 -0
- package/src/Persistence.res +7 -21
- package/src/Persistence.res.mjs +3 -3
- package/src/PgStorage.gen.ts +10 -0
- package/src/PgStorage.res +116 -69
- package/src/PgStorage.res.d.mts +5 -0
- package/src/PgStorage.res.mjs +93 -50
- package/src/Prometheus.res +294 -224
- package/src/Prometheus.res.mjs +353 -340
- package/src/ReorgDetection.res +6 -10
- package/src/ReorgDetection.res.mjs +6 -6
- package/src/SafeCheckpointTracking.res +4 -4
- package/src/SafeCheckpointTracking.res.mjs +2 -2
- package/src/SimulateItems.res +353 -0
- package/src/SimulateItems.res.mjs +335 -0
- package/src/Sink.res +4 -2
- package/src/Sink.res.mjs +2 -1
- package/src/TableIndices.res +0 -1
- package/src/TestIndexer.res +913 -0
- package/src/TestIndexer.res.mjs +698 -0
- package/src/TestIndexerProxyStorage.res +205 -0
- package/src/TestIndexerProxyStorage.res.mjs +151 -0
- package/src/TopicFilter.res +1 -1
- package/src/Types.ts +1 -1
- package/src/UserContext.res +424 -0
- package/src/UserContext.res.mjs +279 -0
- package/src/Utils.res +97 -26
- package/src/Utils.res.mjs +91 -44
- package/src/bindings/BigInt.res +10 -0
- package/src/bindings/BigInt.res.mjs +15 -0
- package/src/bindings/ClickHouse.res +120 -23
- package/src/bindings/ClickHouse.res.mjs +118 -28
- package/src/bindings/DateFns.res +74 -0
- package/src/bindings/DateFns.res.mjs +22 -0
- package/src/bindings/EventSource.res +11 -2
- package/src/bindings/EventSource.res.mjs +8 -1
- package/src/bindings/Express.res +1 -0
- package/src/bindings/Hrtime.res +14 -1
- package/src/bindings/Hrtime.res.mjs +22 -2
- package/src/bindings/Hrtime.resi +4 -0
- package/src/bindings/Lodash.res +0 -1
- package/src/bindings/NodeJs.res +49 -3
- package/src/bindings/NodeJs.res.mjs +11 -3
- package/src/bindings/Pino.res +24 -10
- package/src/bindings/Pino.res.mjs +14 -8
- package/src/bindings/Postgres.gen.ts +8 -0
- package/src/bindings/Postgres.res +5 -1
- package/src/bindings/Postgres.res.d.mts +5 -0
- package/src/bindings/PromClient.res +0 -10
- package/src/bindings/PromClient.res.mjs +0 -3
- package/src/bindings/Vitest.res +144 -0
- package/src/bindings/Vitest.res.mjs +9 -0
- package/src/bindings/WebSocket.res +27 -0
- package/src/bindings/WebSocket.res.mjs +2 -0
- package/src/bindings/Yargs.res +8 -0
- package/src/bindings/Yargs.res.mjs +2 -0
- package/src/db/EntityHistory.res +7 -7
- package/src/db/EntityHistory.res.mjs +9 -9
- package/src/db/InternalTable.res +59 -111
- package/src/db/InternalTable.res.mjs +73 -104
- package/src/db/Table.res +27 -8
- package/src/db/Table.res.mjs +25 -14
- package/src/sources/Evm.res +84 -0
- package/src/sources/Evm.res.mjs +105 -0
- package/src/sources/EvmChain.res +94 -0
- package/src/sources/EvmChain.res.mjs +60 -0
- package/src/sources/Fuel.res +19 -34
- package/src/sources/Fuel.res.mjs +34 -16
- package/src/sources/FuelSDK.res +38 -0
- package/src/sources/FuelSDK.res.mjs +29 -0
- package/src/sources/HyperFuel.res +2 -2
- package/src/sources/HyperFuel.resi +1 -1
- package/src/sources/HyperFuelClient.res +2 -2
- package/src/sources/HyperFuelSource.res +35 -13
- package/src/sources/HyperFuelSource.res.mjs +26 -16
- package/src/sources/HyperSync.res +61 -60
- package/src/sources/HyperSync.res.mjs +53 -67
- package/src/sources/HyperSync.resi +6 -4
- package/src/sources/HyperSyncClient.res +29 -2
- package/src/sources/HyperSyncClient.res.mjs +9 -0
- package/src/sources/HyperSyncHeightStream.res +76 -118
- package/src/sources/HyperSyncHeightStream.res.mjs +68 -75
- package/src/sources/HyperSyncSource.res +122 -143
- package/src/sources/HyperSyncSource.res.mjs +106 -121
- package/src/sources/Rpc.res +86 -14
- package/src/sources/Rpc.res.mjs +101 -9
- package/src/sources/RpcSource.res +731 -364
- package/src/sources/RpcSource.res.mjs +845 -410
- package/src/sources/RpcWebSocketHeightStream.res +181 -0
- package/src/sources/RpcWebSocketHeightStream.res.mjs +196 -0
- package/src/sources/SimulateSource.res +59 -0
- package/src/sources/SimulateSource.res.mjs +50 -0
- package/src/sources/Source.res +7 -5
- package/src/sources/SourceManager.res +358 -221
- package/src/sources/SourceManager.res.mjs +346 -171
- package/src/sources/SourceManager.resi +17 -6
- package/src/sources/Svm.res +81 -0
- package/src/sources/Svm.res.mjs +90 -0
- package/src/tui/Tui.res +247 -0
- package/src/tui/Tui.res.mjs +337 -0
- package/src/tui/bindings/Ink.res +371 -0
- package/src/tui/bindings/Ink.res.mjs +72 -0
- package/src/tui/bindings/Style.res +123 -0
- package/src/tui/bindings/Style.res.mjs +2 -0
- package/src/tui/components/BufferedProgressBar.res +40 -0
- package/src/tui/components/BufferedProgressBar.res.mjs +57 -0
- package/src/tui/components/CustomHooks.res +122 -0
- package/src/tui/components/CustomHooks.res.mjs +179 -0
- package/src/tui/components/Messages.res +41 -0
- package/src/tui/components/Messages.res.mjs +75 -0
- package/src/tui/components/SyncETA.res +174 -0
- package/src/tui/components/SyncETA.res.mjs +263 -0
- package/src/tui/components/TuiData.res +47 -0
- package/src/tui/components/TuiData.res.mjs +34 -0
- package/svm.schema.json +112 -0
- package/bin.js +0 -48
- package/src/EventRegister.res +0 -241
- package/src/EventRegister.res.mjs +0 -240
- package/src/EventRegister.resi +0 -30
- package/src/bindings/Ethers.gen.ts +0 -14
- package/src/bindings/Ethers.res +0 -204
- package/src/bindings/Ethers.res.mjs +0 -130
- /package/src/{Indexer.res.mjs → Ctx.res.mjs} +0 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/*
|
|
2
|
+
WebSocket-based implementation for real-time block height tracking.
|
|
3
|
+
Uses eth_subscribe("newHeads") for low-latency block detection.
|
|
4
|
+
Falls back behavior is handled by SourceManager when subscription fails.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
let retryCount = 9
|
|
8
|
+
let baseDuration = 125
|
|
9
|
+
// Close and reconnect if no new block head arrives within this period.
|
|
10
|
+
// Detects silently dropped server-side subscriptions.
|
|
11
|
+
let staleTimeMillis = 60_000
|
|
12
|
+
|
|
13
|
+
type wsMessage =
|
|
14
|
+
| NewHead(int)
|
|
15
|
+
| SubscriptionConfirmed(string)
|
|
16
|
+
| ErrorResponse
|
|
17
|
+
|
|
18
|
+
let subscribeRequestJson =
|
|
19
|
+
{"jsonrpc": "2.0", "id": 1, "method": "eth_subscribe", "params": ["newHeads"]}
|
|
20
|
+
->(Utils.magic: {
|
|
21
|
+
"jsonrpc": string,
|
|
22
|
+
"id": int,
|
|
23
|
+
"method": string,
|
|
24
|
+
"params": array<string>,
|
|
25
|
+
} => Js.Json.t)
|
|
26
|
+
->Js.Json.serializeExn
|
|
27
|
+
|
|
28
|
+
let wsMessageSchema = S.union([
|
|
29
|
+
S.object(s => {
|
|
30
|
+
let _ = s.field("method", S.literal("eth_subscription"))
|
|
31
|
+
NewHead(
|
|
32
|
+
s.field(
|
|
33
|
+
"params",
|
|
34
|
+
S.object(s => {
|
|
35
|
+
s.field(
|
|
36
|
+
"result",
|
|
37
|
+
S.object(
|
|
38
|
+
s => {
|
|
39
|
+
s.field("number", Rpc.hexIntSchema)
|
|
40
|
+
},
|
|
41
|
+
),
|
|
42
|
+
)
|
|
43
|
+
}),
|
|
44
|
+
),
|
|
45
|
+
)
|
|
46
|
+
}),
|
|
47
|
+
S.object(s => {
|
|
48
|
+
SubscriptionConfirmed(s.field("result", S.string))
|
|
49
|
+
}),
|
|
50
|
+
S.object(s => {
|
|
51
|
+
let _ = s.field("error", S.unknown)
|
|
52
|
+
ErrorResponse
|
|
53
|
+
}),
|
|
54
|
+
])
|
|
55
|
+
|
|
56
|
+
let subscribe = (~wsUrl, ~chainId, ~onHeight: int => unit): (unit => unit) => {
|
|
57
|
+
let wsRef: ref<option<WebSocket.t>> = ref(None)
|
|
58
|
+
let isUnsubscribed = ref(false)
|
|
59
|
+
let errorCount = ref(0)
|
|
60
|
+
let staleTimeoutId: ref<option<Js.Global.timeoutId>> = ref(None)
|
|
61
|
+
|
|
62
|
+
let clearStaleTimeout = () => {
|
|
63
|
+
switch staleTimeoutId.contents {
|
|
64
|
+
| Some(id) =>
|
|
65
|
+
Js.Global.clearTimeout(id)
|
|
66
|
+
staleTimeoutId := None
|
|
67
|
+
| None => ()
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let resetStaleTimeout = () => {
|
|
72
|
+
clearStaleTimeout()
|
|
73
|
+
staleTimeoutId := Some(Js.Global.setTimeout(() => {
|
|
74
|
+
// Connection went stale - close to trigger reconnect
|
|
75
|
+
switch wsRef.contents {
|
|
76
|
+
| Some(ws) => ws->WebSocket.close
|
|
77
|
+
| None => ()
|
|
78
|
+
}
|
|
79
|
+
}, staleTimeMillis))
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let rec scheduleReconnect = () => {
|
|
83
|
+
if !isUnsubscribed.contents && errorCount.contents < retryCount {
|
|
84
|
+
let duration =
|
|
85
|
+
baseDuration *
|
|
86
|
+
Js.Math.pow_float(~base=2.0, ~exp=errorCount.contents->Belt.Int.toFloat)->Belt.Float.toInt
|
|
87
|
+
let _ = Js.Global.setTimeout(() => {
|
|
88
|
+
if !isUnsubscribed.contents {
|
|
89
|
+
startConnection()
|
|
90
|
+
}
|
|
91
|
+
}, duration)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
and startConnection = () => {
|
|
95
|
+
if isUnsubscribed.contents || errorCount.contents >= retryCount {
|
|
96
|
+
()
|
|
97
|
+
} else {
|
|
98
|
+
let ws = WebSocket.create(wsUrl)
|
|
99
|
+
wsRef := Some(ws)
|
|
100
|
+
|
|
101
|
+
ws->WebSocket.onopen(() => {
|
|
102
|
+
ws->WebSocket.send(subscribeRequestJson)
|
|
103
|
+
resetStaleTimeout()
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
ws->WebSocket.onmessage(event => {
|
|
107
|
+
try {
|
|
108
|
+
switch event.data->Js.Json.parseExn->S.parseOrThrow(wsMessageSchema) {
|
|
109
|
+
| NewHead(blockNumber) =>
|
|
110
|
+
errorCount := 0
|
|
111
|
+
resetStaleTimeout()
|
|
112
|
+
Prometheus.SourceRequestCount.increment(
|
|
113
|
+
~sourceName="WebSocket",
|
|
114
|
+
~chainId,
|
|
115
|
+
~method="eth_subscribe",
|
|
116
|
+
)
|
|
117
|
+
onHeight(blockNumber)
|
|
118
|
+
| SubscriptionConfirmed(_) => resetStaleTimeout()
|
|
119
|
+
| ErrorResponse =>
|
|
120
|
+
if errorCount.contents < retryCount {
|
|
121
|
+
errorCount := errorCount.contents + 1
|
|
122
|
+
}
|
|
123
|
+
switch wsRef.contents {
|
|
124
|
+
| Some(ws) => ws->WebSocket.close
|
|
125
|
+
| None => ()
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
} catch {
|
|
129
|
+
| S.Raised(_) =>
|
|
130
|
+
Logging.warn({
|
|
131
|
+
"msg": "WebSocket height stream received unrecognized message",
|
|
132
|
+
"chainId": chainId,
|
|
133
|
+
"data": event.data,
|
|
134
|
+
})
|
|
135
|
+
| Js.Exn.Error(_) as e =>
|
|
136
|
+
Logging.warn({
|
|
137
|
+
"msg": "WebSocket height stream failed to parse message",
|
|
138
|
+
"chainId": chainId,
|
|
139
|
+
"err": e->Utils.prettifyExn,
|
|
140
|
+
"data": event.data,
|
|
141
|
+
})
|
|
142
|
+
| e =>
|
|
143
|
+
Logging.error({
|
|
144
|
+
"msg": "Unexpected error in WebSocket height stream message handler",
|
|
145
|
+
"chainId": chainId,
|
|
146
|
+
"err": e->Utils.prettifyExn,
|
|
147
|
+
"data": event.data,
|
|
148
|
+
})
|
|
149
|
+
raise(e)
|
|
150
|
+
}
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
ws->WebSocket.onerror(_error => {
|
|
154
|
+
if errorCount.contents < retryCount {
|
|
155
|
+
errorCount := errorCount.contents + 1
|
|
156
|
+
}
|
|
157
|
+
switch wsRef.contents {
|
|
158
|
+
| Some(ws) if ws->WebSocket.readyState === Open => ws->WebSocket.close
|
|
159
|
+
| _ => ()
|
|
160
|
+
}
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
ws->WebSocket.onclose(() => {
|
|
164
|
+
wsRef := None
|
|
165
|
+
clearStaleTimeout()
|
|
166
|
+
scheduleReconnect()
|
|
167
|
+
})
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
startConnection()
|
|
172
|
+
|
|
173
|
+
() => {
|
|
174
|
+
isUnsubscribed := true
|
|
175
|
+
clearStaleTimeout()
|
|
176
|
+
switch wsRef.contents {
|
|
177
|
+
| Some(ws) => ws->WebSocket.close
|
|
178
|
+
| None => ()
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Rpc from "./Rpc.res.mjs";
|
|
4
|
+
import * as Utils from "../Utils.res.mjs";
|
|
5
|
+
import * as Js_exn from "rescript/lib/es6/js_exn.js";
|
|
6
|
+
import * as Js_json from "rescript/lib/es6/js_json.js";
|
|
7
|
+
import * as Logging from "../Logging.res.mjs";
|
|
8
|
+
import * as Prometheus from "../Prometheus.res.mjs";
|
|
9
|
+
import * as Caml_option from "rescript/lib/es6/caml_option.js";
|
|
10
|
+
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
11
|
+
import * as Caml_js_exceptions from "rescript/lib/es6/caml_js_exceptions.js";
|
|
12
|
+
|
|
13
|
+
var subscribeRequestJson = Js_json.serializeExn({
|
|
14
|
+
jsonrpc: "2.0",
|
|
15
|
+
id: 1,
|
|
16
|
+
method: "eth_subscribe",
|
|
17
|
+
params: ["newHeads"]
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
var wsMessageSchema = S$RescriptSchema.union([
|
|
21
|
+
S$RescriptSchema.object(function (s) {
|
|
22
|
+
s.f("method", S$RescriptSchema.literal("eth_subscription"));
|
|
23
|
+
return {
|
|
24
|
+
TAG: "NewHead",
|
|
25
|
+
_0: s.f("params", S$RescriptSchema.object(function (s) {
|
|
26
|
+
return s.f("result", S$RescriptSchema.object(function (s) {
|
|
27
|
+
return s.f("number", Rpc.hexIntSchema);
|
|
28
|
+
}));
|
|
29
|
+
}))
|
|
30
|
+
};
|
|
31
|
+
}),
|
|
32
|
+
S$RescriptSchema.object(function (s) {
|
|
33
|
+
return {
|
|
34
|
+
TAG: "SubscriptionConfirmed",
|
|
35
|
+
_0: s.f("result", S$RescriptSchema.string)
|
|
36
|
+
};
|
|
37
|
+
}),
|
|
38
|
+
S$RescriptSchema.object(function (s) {
|
|
39
|
+
s.f("error", S$RescriptSchema.unknown);
|
|
40
|
+
return "ErrorResponse";
|
|
41
|
+
})
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
function subscribe(wsUrl, chainId, onHeight) {
|
|
45
|
+
var wsRef = {
|
|
46
|
+
contents: undefined
|
|
47
|
+
};
|
|
48
|
+
var isUnsubscribed = {
|
|
49
|
+
contents: false
|
|
50
|
+
};
|
|
51
|
+
var errorCount = {
|
|
52
|
+
contents: 0
|
|
53
|
+
};
|
|
54
|
+
var staleTimeoutId = {
|
|
55
|
+
contents: undefined
|
|
56
|
+
};
|
|
57
|
+
var clearStaleTimeout = function () {
|
|
58
|
+
var id = staleTimeoutId.contents;
|
|
59
|
+
if (id !== undefined) {
|
|
60
|
+
clearTimeout(Caml_option.valFromOption(id));
|
|
61
|
+
staleTimeoutId.contents = undefined;
|
|
62
|
+
return ;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
};
|
|
66
|
+
var resetStaleTimeout = function () {
|
|
67
|
+
clearStaleTimeout();
|
|
68
|
+
staleTimeoutId.contents = Caml_option.some(setTimeout((function () {
|
|
69
|
+
var ws = wsRef.contents;
|
|
70
|
+
if (ws !== undefined) {
|
|
71
|
+
Caml_option.valFromOption(ws).close();
|
|
72
|
+
return ;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
}), 60000));
|
|
76
|
+
};
|
|
77
|
+
var scheduleReconnect = function () {
|
|
78
|
+
if (!(!isUnsubscribed.contents && errorCount.contents < 9)) {
|
|
79
|
+
return ;
|
|
80
|
+
}
|
|
81
|
+
var duration = Math.imul(125, Math.pow(2.0, errorCount.contents) | 0);
|
|
82
|
+
setTimeout((function () {
|
|
83
|
+
if (!isUnsubscribed.contents) {
|
|
84
|
+
return startConnection();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
}), duration);
|
|
88
|
+
};
|
|
89
|
+
var startConnection = function () {
|
|
90
|
+
if (isUnsubscribed.contents || errorCount.contents >= 9) {
|
|
91
|
+
return ;
|
|
92
|
+
}
|
|
93
|
+
var ws = new WebSocket(wsUrl);
|
|
94
|
+
wsRef.contents = Caml_option.some(ws);
|
|
95
|
+
ws.onopen = (function () {
|
|
96
|
+
ws.send(subscribeRequestJson);
|
|
97
|
+
resetStaleTimeout();
|
|
98
|
+
});
|
|
99
|
+
ws.onmessage = (function ($$event) {
|
|
100
|
+
try {
|
|
101
|
+
var blockNumber = S$RescriptSchema.parseOrThrow(JSON.parse($$event.data), wsMessageSchema);
|
|
102
|
+
if (typeof blockNumber !== "object") {
|
|
103
|
+
if (errorCount.contents < 9) {
|
|
104
|
+
errorCount.contents = errorCount.contents + 1 | 0;
|
|
105
|
+
}
|
|
106
|
+
var ws = wsRef.contents;
|
|
107
|
+
if (ws !== undefined) {
|
|
108
|
+
Caml_option.valFromOption(ws).close();
|
|
109
|
+
return ;
|
|
110
|
+
} else {
|
|
111
|
+
return ;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (blockNumber.TAG !== "NewHead") {
|
|
115
|
+
return resetStaleTimeout();
|
|
116
|
+
}
|
|
117
|
+
errorCount.contents = 0;
|
|
118
|
+
resetStaleTimeout();
|
|
119
|
+
Prometheus.SourceRequestCount.increment("WebSocket", chainId, "eth_subscribe");
|
|
120
|
+
return onHeight(blockNumber._0);
|
|
121
|
+
}
|
|
122
|
+
catch (raw_e){
|
|
123
|
+
var e = Caml_js_exceptions.internalToOCamlException(raw_e);
|
|
124
|
+
if (e.RE_EXN_ID === S$RescriptSchema.Raised) {
|
|
125
|
+
return Logging.warn({
|
|
126
|
+
msg: "WebSocket height stream received unrecognized message",
|
|
127
|
+
chainId: chainId,
|
|
128
|
+
data: $$event.data
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
if (e.RE_EXN_ID === Js_exn.$$Error) {
|
|
132
|
+
return Logging.warn({
|
|
133
|
+
msg: "WebSocket height stream failed to parse message",
|
|
134
|
+
chainId: chainId,
|
|
135
|
+
err: Utils.prettifyExn(e),
|
|
136
|
+
data: $$event.data
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
Logging.error({
|
|
140
|
+
msg: "Unexpected error in WebSocket height stream message handler",
|
|
141
|
+
chainId: chainId,
|
|
142
|
+
err: Utils.prettifyExn(e),
|
|
143
|
+
data: $$event.data
|
|
144
|
+
});
|
|
145
|
+
throw e;
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
ws.onerror = (function (_error) {
|
|
149
|
+
if (errorCount.contents < 9) {
|
|
150
|
+
errorCount.contents = errorCount.contents + 1 | 0;
|
|
151
|
+
}
|
|
152
|
+
var ws = wsRef.contents;
|
|
153
|
+
if (ws === undefined) {
|
|
154
|
+
return ;
|
|
155
|
+
}
|
|
156
|
+
var ws$1 = Caml_option.valFromOption(ws);
|
|
157
|
+
if (ws$1.readyState === 1) {
|
|
158
|
+
ws$1.close();
|
|
159
|
+
return ;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
});
|
|
163
|
+
ws.onclose = (function () {
|
|
164
|
+
wsRef.contents = undefined;
|
|
165
|
+
clearStaleTimeout();
|
|
166
|
+
scheduleReconnect();
|
|
167
|
+
});
|
|
168
|
+
};
|
|
169
|
+
startConnection();
|
|
170
|
+
return function () {
|
|
171
|
+
isUnsubscribed.contents = true;
|
|
172
|
+
clearStaleTimeout();
|
|
173
|
+
var ws = wsRef.contents;
|
|
174
|
+
if (ws !== undefined) {
|
|
175
|
+
Caml_option.valFromOption(ws).close();
|
|
176
|
+
return ;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
var retryCount = 9;
|
|
183
|
+
|
|
184
|
+
var baseDuration = 125;
|
|
185
|
+
|
|
186
|
+
var staleTimeMillis = 60000;
|
|
187
|
+
|
|
188
|
+
export {
|
|
189
|
+
retryCount ,
|
|
190
|
+
baseDuration ,
|
|
191
|
+
staleTimeMillis ,
|
|
192
|
+
subscribeRequestJson ,
|
|
193
|
+
wsMessageSchema ,
|
|
194
|
+
subscribe ,
|
|
195
|
+
}
|
|
196
|
+
/* subscribeRequestJson Not a pure module */
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
let make = (~items: array<Internal.item>, ~endBlock: int, ~chain: ChainMap.Chain.t): Source.t => {
|
|
2
|
+
// getItemsOrThrow might be called multiple times with different partition ids.
|
|
3
|
+
// Return all items on the first call and empty on subsequent calls to prevent
|
|
4
|
+
// duplicate event processing.
|
|
5
|
+
let delivered = ref(false)
|
|
6
|
+
|
|
7
|
+
{
|
|
8
|
+
name: "SimulateSource",
|
|
9
|
+
sourceFor: Sync,
|
|
10
|
+
chain,
|
|
11
|
+
poweredByHyperSync: false,
|
|
12
|
+
pollingInterval: 0,
|
|
13
|
+
getBlockHashes: (~blockNumbers as _, ~logger as _) => {
|
|
14
|
+
Promise.resolve(Ok([]))
|
|
15
|
+
},
|
|
16
|
+
getHeightOrThrow: () => {
|
|
17
|
+
// Report at least height 1 so the engine doesn't treat 0 as "no blocks available"
|
|
18
|
+
Promise.resolve(max(endBlock, 1))
|
|
19
|
+
},
|
|
20
|
+
getItemsOrThrow: (
|
|
21
|
+
~fromBlock as _,
|
|
22
|
+
~toBlock as _,
|
|
23
|
+
~addressesByContractName as _,
|
|
24
|
+
~indexingContracts as _,
|
|
25
|
+
~knownHeight as _,
|
|
26
|
+
~partitionId as _,
|
|
27
|
+
~selection as _,
|
|
28
|
+
~retry as _,
|
|
29
|
+
~logger as _,
|
|
30
|
+
) => {
|
|
31
|
+
// Return all items on first call, empty on subsequent calls
|
|
32
|
+
let result = if delivered.contents {
|
|
33
|
+
[]
|
|
34
|
+
} else {
|
|
35
|
+
delivered := true
|
|
36
|
+
items
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let reportedHeight = max(endBlock, 1)
|
|
40
|
+
Promise.resolve({
|
|
41
|
+
Source.knownHeight: reportedHeight,
|
|
42
|
+
reorgGuard: {
|
|
43
|
+
rangeLastBlock: {
|
|
44
|
+
blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
45
|
+
blockNumber: reportedHeight,
|
|
46
|
+
},
|
|
47
|
+
prevRangeLastBlock: None,
|
|
48
|
+
},
|
|
49
|
+
parsedQueueItems: result,
|
|
50
|
+
fromBlockQueried: 0,
|
|
51
|
+
latestFetchedBlockNumber: reportedHeight,
|
|
52
|
+
latestFetchedBlockTimestamp: 0,
|
|
53
|
+
stats: {
|
|
54
|
+
totalTimeElapsed: 0.,
|
|
55
|
+
},
|
|
56
|
+
})
|
|
57
|
+
},
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
function make(items, endBlock, chain) {
|
|
5
|
+
var delivered = {
|
|
6
|
+
contents: false
|
|
7
|
+
};
|
|
8
|
+
return {
|
|
9
|
+
name: "SimulateSource",
|
|
10
|
+
sourceFor: "Sync",
|
|
11
|
+
chain: chain,
|
|
12
|
+
poweredByHyperSync: false,
|
|
13
|
+
pollingInterval: 0,
|
|
14
|
+
getBlockHashes: (function (param, param$1) {
|
|
15
|
+
return Promise.resolve({
|
|
16
|
+
TAG: "Ok",
|
|
17
|
+
_0: []
|
|
18
|
+
});
|
|
19
|
+
}),
|
|
20
|
+
getHeightOrThrow: (function () {
|
|
21
|
+
return Promise.resolve(endBlock > 1 ? endBlock : 1);
|
|
22
|
+
}),
|
|
23
|
+
getItemsOrThrow: (function (param, param$1, param$2, param$3, param$4, param$5, param$6, param$7, param$8) {
|
|
24
|
+
var result = delivered.contents ? [] : (delivered.contents = true, items);
|
|
25
|
+
var reportedHeight = endBlock > 1 ? endBlock : 1;
|
|
26
|
+
return Promise.resolve({
|
|
27
|
+
knownHeight: reportedHeight,
|
|
28
|
+
reorgGuard: {
|
|
29
|
+
rangeLastBlock: {
|
|
30
|
+
blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
31
|
+
blockNumber: reportedHeight
|
|
32
|
+
},
|
|
33
|
+
prevRangeLastBlock: undefined
|
|
34
|
+
},
|
|
35
|
+
parsedQueueItems: result,
|
|
36
|
+
fromBlockQueried: 0,
|
|
37
|
+
latestFetchedBlockNumber: reportedHeight,
|
|
38
|
+
latestFetchedBlockTimestamp: 0,
|
|
39
|
+
stats: {
|
|
40
|
+
"total time elapsed (s)": 0
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
})
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
make ,
|
|
49
|
+
}
|
|
50
|
+
/* No side effect */
|
package/src/sources/Source.res
CHANGED
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
A set of stats for logging about the block range fetch
|
|
3
3
|
*/
|
|
4
4
|
type blockRangeFetchStats = {
|
|
5
|
-
@as("total time elapsed (
|
|
6
|
-
@as("parsing time (
|
|
7
|
-
@as("page fetch time (
|
|
5
|
+
@as("total time elapsed (s)") totalTimeElapsed: float,
|
|
6
|
+
@as("parsing time (s)") parsingTimeElapsed?: float,
|
|
7
|
+
@as("page fetch time (s)") pageFetchTime?: float,
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
Thes response returned from a block range fetch
|
|
12
12
|
*/
|
|
13
13
|
type blockRangeFetchResponse = {
|
|
14
|
-
|
|
14
|
+
knownHeight: int,
|
|
15
15
|
reorgGuard: ReorgDetection.reorgGuard,
|
|
16
16
|
parsedQueueItems: array<Internal.item>,
|
|
17
17
|
fromBlockQueried: int,
|
|
@@ -33,6 +33,7 @@ type getItemsError =
|
|
|
33
33
|
exception GetItemsError(getItemsError)
|
|
34
34
|
|
|
35
35
|
type sourceFor = Sync | Fallback | Live
|
|
36
|
+
|
|
36
37
|
type t = {
|
|
37
38
|
name: string,
|
|
38
39
|
sourceFor: sourceFor,
|
|
@@ -50,10 +51,11 @@ type t = {
|
|
|
50
51
|
~toBlock: option<int>,
|
|
51
52
|
~addressesByContractName: dict<array<Address.t>>,
|
|
52
53
|
~indexingContracts: dict<Internal.indexingContract>,
|
|
53
|
-
~
|
|
54
|
+
~knownHeight: int,
|
|
54
55
|
~partitionId: string,
|
|
55
56
|
~selection: FetchState.selection,
|
|
56
57
|
~retry: int,
|
|
57
58
|
~logger: Pino.t,
|
|
58
59
|
) => promise<blockRangeFetchResponse>,
|
|
60
|
+
createHeightSubscription?: (~onHeight: int => unit) => unit => unit,
|
|
59
61
|
}
|