UNPKG

envio

Version:

A latency and sync speed optimized, developer friendly blockchain data indexer.

183 lines (174 loc) • 5.1 kB
// 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 */