envio
Version:
A latency and sync speed optimized, developer friendly blockchain data indexer.
414 lines (405 loc) • 18.1 kB
JavaScript
// Generated by ReScript, PLEASE EDIT WITH CARE
;
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 */