UNPKG

envio

Version:

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

414 lines (405 loc) • 18.1 kB
// Generated by ReScript, PLEASE EDIT WITH CARE 'use strict'; var Rest = require("../vendored/Rest.res.js"); var Viem = require("../bindings/Viem.res.js"); var Utils = require("../Utils.res.js"); var Hrtime = require("../bindings/Hrtime.res.js"); var Js_exn = require("rescript/lib/js/js_exn.js"); var Source = require("./Source.res.js"); var Logging = require("../Logging.res.js"); var HyperSync = require("./HyperSync.res.js"); var Belt_Array = require("rescript/lib/js/belt_Array.js"); var Belt_Option = require("rescript/lib/js/belt_Option.js"); var EventRouter = require("./EventRouter.res.js"); var LogSelection = require("../LogSelection.res.js"); var ErrorHandling = require("../ErrorHandling.res.js"); var Caml_exceptions = require("rescript/lib/js/caml_exceptions.js"); var HyperSyncClient = require("./HyperSyncClient.res.js"); var Caml_splice_call = require("rescript/lib/js/caml_splice_call.js"); var HyperSyncJsonApi = require("./HyperSyncJsonApi.res.js"); var Caml_js_exceptions = require("rescript/lib/js/caml_js_exceptions.js"); function getSelectionConfig(selection, chain) { var nonOptionalBlockFieldNames = new Set(); var nonOptionalTransactionFieldNames = new Set(); var capitalizedBlockFields = new Set(); var capitalizedTransactionFields = new Set(); var staticTopicSelectionsByContract = {}; var dynamicEventFiltersByContract = {}; var dynamicWildcardEventFiltersByContract = {}; var noAddressesTopicSelections = []; var contractNames = new Set(); Belt_Array.forEach(selection.eventConfigs, (function (param) { var transactionSchema = param.transactionSchema; var blockSchema = param.blockSchema; var contractName = param.contractName; Utils.$$Set.addMany(nonOptionalBlockFieldNames, Utils.Schema.getNonOptionalFieldNames(blockSchema)); Utils.$$Set.addMany(nonOptionalTransactionFieldNames, Utils.Schema.getNonOptionalFieldNames(transactionSchema)); Utils.$$Set.addMany(capitalizedBlockFields, Utils.Schema.getCapitalizedFieldNames(blockSchema)); Utils.$$Set.addMany(capitalizedTransactionFields, Utils.Schema.getCapitalizedFieldNames(transactionSchema)); var eventFilters = param.getEventFiltersOrThrow(chain); if (param.dependsOnAddresses) { contractNames.add(contractName); if (eventFilters.TAG === "Static") { return Utils.Dict.pushMany(staticTopicSelectionsByContract, contractName, eventFilters._0); } else { return Utils.Dict.push(param.isWildcard ? dynamicWildcardEventFiltersByContract : dynamicEventFiltersByContract, contractName, eventFilters._0); } } var tmp; tmp = eventFilters.TAG === "Static" ? eventFilters._0 : eventFilters._0([]); Caml_splice_call.spliceObjApply(noAddressesTopicSelections, "push", [tmp]); })); var fieldSelection_block = Array.from(capitalizedBlockFields); var fieldSelection_transaction = Array.from(capitalizedTransactionFields); var fieldSelection_log = [ "Address", "Data", "LogIndex", "Topic0", "Topic1", "Topic2", "Topic3" ]; var fieldSelection = { block: fieldSelection_block, transaction: fieldSelection_transaction, log: fieldSelection_log }; var noAddressesLogSelection = LogSelection.make([], noAddressesTopicSelections); var getLogSelectionOrThrow = function (addressesByContractName) { var logSelections = []; if (!Utils.$$Array.isEmpty(noAddressesLogSelection.topicSelections)) { logSelections.push(noAddressesLogSelection); } contractNames.forEach(function (contractName) { var addresses = addressesByContractName[contractName]; if (addresses === undefined) { return ; } if (addresses.length === 0) { return ; } var topicSelections = staticTopicSelectionsByContract[contractName]; if (topicSelections !== undefined) { logSelections.push(LogSelection.make(addresses, topicSelections)); } var fns = dynamicEventFiltersByContract[contractName]; if (fns !== undefined) { logSelections.push(LogSelection.make(addresses, Belt_Array.flatMapU(fns, (function (fn) { return fn(addresses); })))); } var fns$1 = dynamicWildcardEventFiltersByContract[contractName]; if (fns$1 !== undefined) { logSelections.push(LogSelection.make([], Belt_Array.flatMapU(fns$1, (function (fn) { return fn(addresses); })))); return ; } }); return logSelections; }; return { getLogSelectionOrThrow: getLogSelectionOrThrow, fieldSelection: fieldSelection, nonOptionalBlockFieldNames: Array.from(nonOptionalBlockFieldNames), nonOptionalTransactionFieldNames: Array.from(nonOptionalTransactionFieldNames) }; } function memoGetSelectionConfig(chain) { var cache = new WeakMap(); return function (selection) { var c = cache.get(selection); if (c !== undefined) { return c; } var c$1 = getSelectionConfig(selection, chain); cache.set(selection, c$1); return c$1; }; } function make(param) { var eventRouter = param.eventRouter; var shouldUseHypersyncClientDecoder = param.shouldUseHypersyncClientDecoder; var allEventSignatures = param.allEventSignatures; var endpointUrl = param.endpointUrl; var chain = param.chain; var getSelectionConfig = memoGetSelectionConfig(chain); var apiToken = Belt_Option.getWithDefault(param.apiToken, "3dc856dd-b0ea-494f-b27e-017b8b6b7e07"); var client = HyperSyncClient.make(endpointUrl, apiToken, param.clientTimeoutMillis, param.clientMaxRetries); var hscDecoder = { contents: undefined }; var getHscDecoder = function () { var decoder = hscDecoder.contents; if (decoder !== undefined) { return decoder; } var decoder$1; try { decoder$1 = HyperSyncClient.Decoder.fromSignatures(allEventSignatures); } catch (raw_exn){ var exn = Caml_js_exceptions.internalToOCamlException(raw_exn); return ErrorHandling.mkLogAndRaise(undefined, "Failed to instantiate a decoder from hypersync client, please double check your ABI or try using 'event_decoder: viem' config option", exn); } decoder$1.enableChecksummedAddresses(); return decoder$1; }; var UndefinedValue = /* @__PURE__ */Caml_exceptions.create("UndefinedValue"); var makeEventBatchQueueItem = function (item, params, eventConfig) { var block = item.block; var log = item.log; return { kind: 0, eventConfig: eventConfig, timestamp: block.timestamp, chain: chain, blockNumber: block.number, logIndex: log.logIndex, event: { params: params, chainId: chain, srcAddress: log.address, logIndex: log.logIndex, transaction: item.transaction, block: block } }; }; var contractNameAbiMapping = {}; Belt_Array.forEach(param.contracts, (function (contract) { contractNameAbiMapping[contract.name] = contract.abi; })); var getItemsOrThrow = async function (fromBlock, toBlock, addressesByContractName, indexingContracts, currentBlockHeight, param, selection, retry, logger) { var mkLogAndRaise = function (extra, extra$1) { return ErrorHandling.mkLogAndRaise(logger, extra, extra$1); }; var totalTimeRef = Hrtime.makeTimer(); var selectionConfig = getSelectionConfig(selection); var logSelections; try { logSelections = selectionConfig.getLogSelectionOrThrow(addressesByContractName); } catch (raw_exn){ var exn = Caml_js_exceptions.internalToOCamlException(raw_exn); logSelections = ErrorHandling.mkLogAndRaise(logger, "Failed getting log selection for the query", exn); } var startFetchingBatchTimeRef = Hrtime.makeTimer(); var pageUnsafe; try { pageUnsafe = await HyperSync.GetLogs.query(client, fromBlock, toBlock, logSelections, selectionConfig.fieldSelection, selectionConfig.nonOptionalBlockFieldNames, selectionConfig.nonOptionalTransactionFieldNames); } catch (raw_error){ var error = Caml_js_exceptions.internalToOCamlException(raw_error); if (error.RE_EXN_ID === HyperSync.GetLogs.$$Error) { var error$1 = error._1; var tmp; if (typeof error$1 !== "object") { var backoffMillis = retry !== 0 ? Math.imul(500, retry) : 100; tmp = { TAG: "WithBackoff", message: "Block #" + String(fromBlock) + " 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 " + String(backoffMillis) + "ms.", backoffMillis: backoffMillis }; } else { tmp = { TAG: "WithBackoff", message: "Received page response with invalid data. Attempt a retry. Missing params: " + error$1.missingParams.join(","), backoffMillis: retry !== 0 ? Math.imul(4000, retry) : 1000 }; } throw { RE_EXN_ID: Source.GetItemsError, _1: { TAG: "FailedGettingItems", exn: null, attemptedToBlock: Belt_Option.getWithDefault(toBlock, currentBlockHeight), retry: tmp }, Error: new Error() }; } throw { RE_EXN_ID: Source.GetItemsError, _1: { TAG: "FailedGettingItems", exn: error, attemptedToBlock: Belt_Option.getWithDefault(toBlock, currentBlockHeight), retry: { TAG: "WithBackoff", message: "Unexpected issue while fetching events from HyperSync client. Attempt a retry.", backoffMillis: retry !== 0 ? Math.imul(1000, retry) : 500 } }, Error: new Error() }; } var pageFetchTime = Hrtime.intFromMillis(Hrtime.toMillis(Hrtime.timeSince(startFetchingBatchTimeRef))); var currentBlockHeight$1 = pageUnsafe.archiveHeight; var heighestBlockQueried = pageUnsafe.nextBlock - 1 | 0; var match = pageUnsafe.rollbackGuard; var lastBlockQueriedPromise; if (match !== undefined) { lastBlockQueriedPromise = Promise.resolve({ blockHash: match.hash, blockNumber: match.blockNumber, blockTimestamp: match.timestamp }); } else { var match$1 = Belt_Array.get(pageUnsafe.items, pageUnsafe.items.length - 1 | 0); var exit = 0; if (match$1 !== undefined) { var block = match$1.block; if (block.number === heighestBlockQueried) { lastBlockQueriedPromise = Promise.resolve({ blockHash: block.hash, blockNumber: block.number, blockTimestamp: block.timestamp }); } else { exit = 1; } } else { exit = 1; } if (exit === 1) { lastBlockQueriedPromise = HyperSync.queryBlockData(endpointUrl, apiToken, heighestBlockQueried, logger).then(function (res) { if (res.TAG !== "Ok") { return mkLogAndRaise("Failed to query blockData for block " + String(heighestBlockQueried), HyperSync.queryErrorToMsq(res._0)); } var blockData = res._0; if (blockData !== undefined) { return blockData; } else { return mkLogAndRaise("Failure, blockData for block " + String(heighestBlockQueried) + " unexpectedly returned None", { RE_EXN_ID: "Not_found" }); } }); } } var parsingTimeRef = Hrtime.makeTimer(); var parsedQueueItems = []; var handleDecodeFailure = function (eventConfig, decoder, logIndex, blockNumber, chainId, exn) { if (eventConfig.isWildcard) { return ; } var msg = "Event " + eventConfig.name + " was unexpectedly parsed as undefined"; var logger$1 = Logging.createChildFrom(logger, { chainId: chainId, blockNumber: blockNumber, logIndex: logIndex, decoder: decoder }); ErrorHandling.mkLogAndRaise(logger$1, msg, exn); }; if (shouldUseHypersyncClientDecoder) { var parsedEvents; try { parsedEvents = await getHscDecoder().decodeEvents(pageUnsafe.events); } catch (raw_exn$1){ var exn$1 = Caml_js_exceptions.internalToOCamlException(raw_exn$1); parsedEvents = mkLogAndRaise("Failed to parse events using hypersync client, please double check your ABI.", exn$1); } Belt_Array.forEachWithIndex(pageUnsafe.items, (function (index, item) { var block = item.block; var log = item.log; var topic0 = log.topics[0]; var maybeEventConfig = EventRouter.get(eventRouter, EventRouter.getEvmEventId(topic0, log.topics.length), log.address, block.number, indexingContracts); var maybeDecodedEvent = parsedEvents[index]; if (maybeEventConfig === undefined) { return ; } if (maybeDecodedEvent === null || maybeDecodedEvent === undefined) { maybeDecodedEvent === null; } else { parsedQueueItems.push(makeEventBatchQueueItem(item, maybeEventConfig.convertHyperSyncEventArgs(maybeDecodedEvent), maybeEventConfig)); return ; } handleDecodeFailure(maybeEventConfig, "hypersync-client", log.logIndex, block.number, chain, { RE_EXN_ID: UndefinedValue }); })); } else { Belt_Array.forEach(pageUnsafe.items, (function (item) { var block = item.block; var log = item.log; var topic0 = log.topics[0]; var eventConfig = EventRouter.get(eventRouter, EventRouter.getEvmEventId(topic0, log.topics.length), log.address, block.number, indexingContracts); if (eventConfig === undefined) { return ; } var decodedEvent; try { decodedEvent = Viem.parseLogOrThrow(contractNameAbiMapping, eventConfig.contractName, log.topics, log.data); } catch (raw_exn){ var exn = Caml_js_exceptions.internalToOCamlException(raw_exn); return handleDecodeFailure(eventConfig, "viem", log.logIndex, block.number, chain, exn); } parsedQueueItems.push(makeEventBatchQueueItem(item, decodedEvent.args, eventConfig)); })); } var parsingTimeElapsed = Hrtime.intFromMillis(Hrtime.toMillis(Hrtime.timeSince(parsingTimeRef))); var rangeLastBlock = await lastBlockQueriedPromise; var reorgGuard_prevRangeLastBlock = Belt_Option.map(pageUnsafe.rollbackGuard, (function (v) { return { blockHash: v.firstParentHash, blockNumber: v.firstBlockNumber - 1 | 0 }; })); var reorgGuard = { rangeLastBlock: rangeLastBlock, prevRangeLastBlock: reorgGuard_prevRangeLastBlock }; var totalTimeElapsed = Hrtime.intFromMillis(Hrtime.toMillis(Hrtime.timeSince(totalTimeRef))); var stats_parsing$unknowntime$unknown$lparms$rpar = parsingTimeElapsed; var stats_page$unknownfetch$unknowntime$unknown$lparms$rpar = pageFetchTime; var stats = { "total time elapsed (ms)": totalTimeElapsed, "parsing time (ms)": stats_parsing$unknowntime$unknown$lparms$rpar, "page fetch time (ms)": stats_page$unknownfetch$unknowntime$unknown$lparms$rpar }; return { currentBlockHeight: currentBlockHeight$1, reorgGuard: reorgGuard, parsedQueueItems: parsedQueueItems, fromBlockQueried: fromBlock, latestFetchedBlockNumber: rangeLastBlock.blockNumber, latestFetchedBlockTimestamp: rangeLastBlock.blockTimestamp, stats: stats }; }; var getBlockHashes = function (blockNumbers, logger) { return HyperSync.queryBlockDataMulti(endpointUrl, apiToken, blockNumbers, logger).then(HyperSync.mapExn); }; var jsonApiClient = Rest.client(endpointUrl, undefined); return { name: "HyperSync", sourceFor: "Sync", chain: chain, poweredByHyperSync: true, pollingInterval: 100, getBlockHashes: getBlockHashes, getHeightOrThrow: (async function () { var height = await Rest.$$fetch(HyperSyncJsonApi.heightRoute, apiToken, jsonApiClient); if (typeof height === "number") { return height; } else if (height === "Your token is malformed. For more info: https://docs.envio.dev/docs/HyperSync/api-tokens.") { Logging.error("Your ENVIO_API_TOKEN is malformed. The indexer will not be able to fetch events. Update the token and restart the indexer using 'pnpm envio start'. For more info: https://docs.envio.dev/docs/HyperSync/api-tokens"); await new Promise((function (param, param$1) { })); return 0; } else { return Js_exn.raiseError(height); } }), getItemsOrThrow: getItemsOrThrow }; } exports.getSelectionConfig = getSelectionConfig; exports.memoGetSelectionConfig = memoGetSelectionConfig; exports.make = make; /* Rest Not a pure module */