UNPKG

envio

Version:

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

279 lines (262 loc) • 8.77 kB
// 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 */