envio
Version:
A latency and sync speed optimized, developer friendly blockchain data indexer.
279 lines (262 loc) • 8.77 kB
JavaScript
// Generated by ReScript, PLEASE EDIT WITH CARE
import * as Time from "../Time.res.mjs";
import * as Utils from "../Utils.res.mjs";
import * as Source from "./Source.res.mjs";
import * as Logging from "../Logging.res.mjs";
import * as Prometheus from "../Prometheus.res.mjs";
import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
import * as Stdlib_Result from "@rescript/runtime/lib/es6/Stdlib_Result.js";
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
import * as HyperSyncClient from "./HyperSyncClient.res.mjs";
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
function reraisIfRateLimited(exn) {
let e = Primitive_exceptions.internalToException(exn);
if (e.RE_EXN_ID !== "JsExn") {
return;
}
let msg = Stdlib_JsExn.message(e._1);
if (msg === undefined) {
return;
}
if (!msg.startsWith("RATE_LIMITED:")) {
return;
}
let resetMs = Stdlib_Option.getOr(Stdlib_Int.fromString(msg.slice(13, msg.length), undefined), 1000);
throw {
RE_EXN_ID: Source.RateLimited,
resetMs: resetMs,
Error: new Error()
};
}
let HyperSyncQueryError = /* @__PURE__ */Primitive_exceptions.create("HyperSync.HyperSyncQueryError");
function queryErrorToMsq(e) {
let match = e._0;
return match.queryName + ` query failed due to unexpected missing params on response:
` + match.missingParams.join(", ");
}
function mapExn(queryResponse) {
if (queryResponse.TAG === "Ok") {
return {
TAG: "Ok",
_0: queryResponse._0
};
} else {
return {
TAG: "Error",
_0: {
RE_EXN_ID: HyperSyncQueryError,
_1: queryResponse._0
}
};
}
}
let $$Error = /* @__PURE__ */Primitive_exceptions.create("HyperSync.GetLogs.Error");
function makeRequestBody(fromBlock, toBlockInclusive, addressesWithTopics, fieldSelection) {
return {
fromBlock: fromBlock,
toBlock: toBlockInclusive !== undefined ? toBlockInclusive + 1 | 0 : undefined,
logs: addressesWithTopics,
fieldSelection: fieldSelection
};
}
function extractMissingParams(exn) {
let message = exn.RE_EXN_ID === "JsExn" ? Stdlib_JsExn.message(exn._1) : undefined;
if (message === undefined) {
return;
}
let obj;
try {
obj = Stdlib_JSON.Decode.object(JSON.parse(message));
} catch (exn$1) {
return;
}
if (obj === undefined) {
return;
}
let match = obj["kind"];
let match$1 = obj["fields"];
if (match === "MissingFields" && Array.isArray(match$1)) {
return Stdlib_Array.filterMap(match$1, Stdlib_JSON.Decode.string);
}
}
async function query(client, fromBlock, toBlock, logSelections, fieldSelection) {
let addressesWithTopics = logSelections.flatMap(param => {
let addresses = param.addresses;
return param.topicSelections.map(param => {
let topics = HyperSyncClient.QueryTypes.makeTopicSelection(param.topic0, param.topic1, param.topic2, param.topic3);
return HyperSyncClient.QueryTypes.makeLogSelection(addresses, topics);
});
});
let query$1 = makeRequestBody(fromBlock, toBlock, addressesWithTopics, fieldSelection);
let res;
try {
res = await client.getEventItems(query$1);
} catch (raw_exn) {
let exn = Primitive_exceptions.internalToException(raw_exn);
reraisIfRateLimited(exn);
let missingParams = extractMissingParams(exn);
if (missingParams !== undefined) {
throw {
RE_EXN_ID: $$Error,
_1: {
TAG: "UnexpectedMissingParams",
missingParams: missingParams
},
Error: new Error()
};
}
throw exn;
}
if (res.nextBlock <= fromBlock) {
throw {
RE_EXN_ID: $$Error,
_1: "WrongInstance",
Error: new Error()
};
}
return {
items: res.items,
nextBlock: res.nextBlock,
archiveHeight: Stdlib_Option.getOr(res.archiveHeight, 0),
rollbackGuard: res.rollbackGuard
};
}
function makeRequestBody$1(fromBlock, toBlock) {
return {
fromBlock: fromBlock,
toBlock: toBlock + 1 | 0,
fieldSelection: {
block: [
"Number",
"Hash",
"Timestamp"
]
},
includeAllBlocks: true
};
}
function convertResponse(res) {
return Utils.$$Array.transposeResults(res.data.blocks.map(block => {
let blockNumber = block.number;
if (blockNumber !== undefined) {
let blockHash = block.hash;
if (blockHash !== undefined) {
let blockTimestamp = block.timestamp;
if (blockTimestamp !== undefined) {
return {
TAG: "Ok",
_0: {
blockHash: blockHash,
blockNumber: blockNumber,
blockTimestamp: blockTimestamp
}
};
}
}
}
let missingParams = Stdlib_Array.filterMap([
Utils.Option.mapNone(block.number, "block.number"),
Utils.Option.mapNone(block.timestamp, "block.timestamp"),
Utils.Option.mapNone(block.hash, "block.hash")
], p => p);
return {
TAG: "Error",
_0: {
TAG: "UnexpectedMissingParams",
_0: {
queryName: "query block data HyperSync",
missingParams: missingParams
}
}
};
}));
}
async function queryBlockData(client, fromBlock, toBlock, sourceName, chainId, logger) {
let body = makeRequestBody$1(fromBlock, toBlock);
let logger$1 = Logging.createChildFrom(logger, {
logType: "HyperSync get block hash query",
fromBlock: fromBlock,
toBlock: toBlock
});
Prometheus.SourceRequestCount.increment(sourceName, chainId, "getBlockHashes");
let maybeSuccessfulRes;
let exit = 0;
let res;
try {
res = await client.get(body);
exit = 1;
} catch (raw_exn) {
let exn = Primitive_exceptions.internalToException(raw_exn);
reraisIfRateLimited(exn);
maybeSuccessfulRes = undefined;
}
if (exit === 1) {
maybeSuccessfulRes = res.nextBlock <= fromBlock ? undefined : res;
}
if (maybeSuccessfulRes !== undefined) {
let err = convertResponse(maybeSuccessfulRes);
if (err.TAG !== "Ok") {
return err;
}
if (maybeSuccessfulRes.nextBlock > toBlock) {
return err;
}
let datas = err._0;
let restRes = await queryBlockData(client, maybeSuccessfulRes.nextBlock, toBlock, sourceName, chainId, logger$1);
return Stdlib_Result.map(restRes, rest => datas.concat(rest));
}
Logging.childInfo(logger$1, `Block #` + fromBlock.toString() + ` not found in HyperSync. HyperSync has multiple instances and it's possible that they drift independently slightly from the head. Indexing should continue correctly after retrying the query in ` + (100).toString() + `ms.`);
await Time.resolvePromiseAfterDelay(100);
return await queryBlockData(client, fromBlock, toBlock, sourceName, chainId, logger$1);
}
async function queryBlockDataMulti(client, blockNumbers, sourceName, chainId, logger) {
let firstBlock = blockNumbers[0];
if (firstBlock === undefined) {
return {
TAG: "Ok",
_0: []
};
}
let fromBlock = firstBlock;
let toBlock = firstBlock;
let set = new Set();
for (let idx = 0, idx_finish = blockNumbers.length; idx < idx_finish; ++idx) {
let blockNumber = blockNumbers[idx];
if (blockNumber < fromBlock) {
fromBlock = blockNumber;
}
if (blockNumber > toBlock) {
toBlock = blockNumber;
}
set.add(blockNumber);
}
if ((toBlock - fromBlock | 0) > 1000) {
Stdlib_JsError.throwWithMessage(`Invalid block data request. Range of block numbers is too large. Max range is 1000. Requested range: ` + fromBlock.toString() + `-` + toBlock.toString());
}
let res = await queryBlockData(client, fromBlock, toBlock, sourceName, chainId, logger);
let filtered = Stdlib_Result.map(res, datas => datas.filter(data => set.delete(data.blockNumber)));
if (set.size > 0) {
Stdlib_JsError.throwWithMessage(`Invalid response. Failed to get block data for block numbers: ` + Array.from(set).join(", "));
}
return filtered;
}
function queryBlockData$1(client, blockNumber, sourceName, chainId, logger) {
return queryBlockData(client, blockNumber, blockNumber, sourceName, chainId, logger).then(res => Stdlib_Result.map(res, res => res[0]));
}
let GetLogs = {
$$Error: $$Error,
extractMissingParams: extractMissingParams,
query: query
};
export {
queryErrorToMsq,
GetLogs,
queryBlockData$1 as queryBlockData,
queryBlockDataMulti,
mapExn,
}
/* Time Not a pure module */