envio
Version:
A latency and sync speed optimized, developer friendly blockchain data indexer.
183 lines (174 loc) • 5.1 kB
JavaScript
// Generated by ReScript, PLEASE EDIT WITH CARE
import * as Rpc from "./Rpc.res.mjs";
import * as Utils from "../Utils.res.mjs";
import * as Logging from "../Logging.res.mjs";
import * as Prometheus from "../Prometheus.res.mjs";
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
let subscribeRequestJson = JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "eth_subscribe",
params: ["newHeads"]
});
let wsMessageSchema = S$RescriptSchema.union([
S$RescriptSchema.object(s => {
s.f("method", S$RescriptSchema.literal("eth_subscription"));
return {
TAG: "NewHead",
_0: s.f("params", S$RescriptSchema.object(s => s.f("result", S$RescriptSchema.object(s => s.f("number", Rpc.hexIntSchema)))))
};
}),
S$RescriptSchema.object(s => ({
TAG: "SubscriptionConfirmed",
_0: s.f("result", S$RescriptSchema.string)
})),
S$RescriptSchema.object(s => {
s.f("error", S$RescriptSchema.unknown);
return "ErrorResponse";
})
]);
function subscribe(wsUrl, chainId, onHeight) {
let wsRef = {
contents: undefined
};
let isUnsubscribed = {
contents: false
};
let errorCount = {
contents: 0
};
let staleTimeoutId = {
contents: undefined
};
let clearStaleTimeout = () => {
let id = staleTimeoutId.contents;
if (id !== undefined) {
clearTimeout(Primitive_option.valFromOption(id));
staleTimeoutId.contents = undefined;
return;
}
};
let resetStaleTimeout = () => {
clearStaleTimeout();
staleTimeoutId.contents = Primitive_option.some(setTimeout(() => {
let ws = wsRef.contents;
if (ws !== undefined) {
Primitive_option.valFromOption(ws).close();
return;
}
}, 60000));
};
let scheduleReconnect = () => {
if (!(!isUnsubscribed.contents && errorCount.contents < 9)) {
return;
}
let duration = 125 * (Math.pow(2.0, errorCount.contents) | 0) | 0;
setTimeout(() => {
if (!isUnsubscribed.contents) {
return startConnection();
}
}, duration);
};
let startConnection = () => {
if (isUnsubscribed.contents || errorCount.contents >= 9) {
return;
}
let ws = new WebSocket(wsUrl);
wsRef.contents = Primitive_option.some(ws);
ws.onopen = () => {
ws.send(subscribeRequestJson);
resetStaleTimeout();
};
ws.onmessage = event => {
try {
let blockNumber = S$RescriptSchema.parseOrThrow(JSON.parse(event.data), wsMessageSchema);
if (typeof blockNumber !== "object") {
if (errorCount.contents < 9) {
errorCount.contents = errorCount.contents + 1 | 0;
}
let ws = wsRef.contents;
if (ws !== undefined) {
Primitive_option.valFromOption(ws).close();
return;
} else {
return;
}
}
if (blockNumber.TAG !== "NewHead") {
return resetStaleTimeout();
}
errorCount.contents = 0;
resetStaleTimeout();
Prometheus.SourceRequestCount.increment("WebSocket", chainId, "eth_subscribe");
return onHeight(blockNumber._0);
} catch (raw_e) {
let e = Primitive_exceptions.internalToException(raw_e);
if (e.RE_EXN_ID === S$RescriptSchema.Raised) {
return Logging.warn({
msg: "WebSocket height stream received unrecognized message",
chainId: chainId,
data: event.data
});
}
if (e.RE_EXN_ID === "JsExn") {
return Logging.warn({
msg: "WebSocket height stream failed to parse message",
chainId: chainId,
err: Utils.prettifyExn(e),
data: event.data
});
}
Logging.error({
msg: "Unexpected error in WebSocket height stream message handler",
chainId: chainId,
err: Utils.prettifyExn(e),
data: event.data
});
throw e;
}
};
ws.onerror = _error => {
if (errorCount.contents < 9) {
errorCount.contents = errorCount.contents + 1 | 0;
}
let ws = wsRef.contents;
if (ws === undefined) {
return;
}
let ws$1 = Primitive_option.valFromOption(ws);
if (ws$1.readyState === 1) {
ws$1.close();
return;
}
};
ws.onclose = () => {
wsRef.contents = undefined;
clearStaleTimeout();
scheduleReconnect();
};
};
startConnection();
return () => {
isUnsubscribed.contents = true;
clearStaleTimeout();
let ws = wsRef.contents;
if (ws !== undefined) {
Primitive_option.valFromOption(ws).close();
return;
}
};
}
let retryCount = 9;
let baseDuration = 125;
let staleTimeMillis = 60000;
export {
retryCount,
baseDuration,
staleTimeMillis,
subscribeRequestJson,
wsMessageSchema,
subscribe,
}
/* subscribeRequestJson Not a pure module */