UNPKG

envio

Version:

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

426 lines (411 loc) • 14.6 kB
// Generated by ReScript, PLEASE EDIT WITH CARE import * as Rest from "../vendored/Rest.res.mjs"; import * as Hrtime from "../bindings/Hrtime.res.mjs"; import * as Source from "./Source.res.mjs"; import * as Logging from "../Logging.res.mjs"; import * as HyperFuel from "./HyperFuel.res.mjs"; import * as HyperSync from "./HyperSync.res.mjs"; import * as Prometheus from "../Prometheus.res.mjs"; import * as EventRouter from "./EventRouter.res.mjs"; import * as ErrorHandling from "../ErrorHandling.res.mjs"; import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js"; import * as EnvioApiClient from "./EnvioApiClient.res.mjs"; import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js"; import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js"; let EventRoutingFailed = /* @__PURE__ */Primitive_exceptions.create("HyperFuelSource.EventRoutingFailed"); let mintEventTag = "mint"; let burnEventTag = "burn"; let transferEventTag = "transfer"; let callEventTag = "call"; let logDataReceiptTypeSelection = [6]; let txStatusSelection = [1]; function makeGetNormalRecieptsSelection(nonWildcardLogDataRbsByContract, nonLogDataReceiptTypesByContract, contractNames) { return addressesByContractName => { let selection = []; contractNames.forEach(contractName => { let addresses = addressesByContractName[contractName]; if (addresses === undefined) { return; } if (addresses.length === 0) { return; } let receiptTypes = nonLogDataReceiptTypesByContract[contractName]; if (receiptTypes !== undefined) { selection.push({ rootContractId: addresses, receiptType: receiptTypes, txStatus: txStatusSelection }); } let nonWildcardLogDataRbs = nonWildcardLogDataRbsByContract[contractName]; if (nonWildcardLogDataRbs !== undefined && nonWildcardLogDataRbs.length !== 0) { selection.push({ rootContractId: addresses, receiptType: logDataReceiptTypeSelection, rb: nonWildcardLogDataRbs, txStatus: txStatusSelection }); return; } }); return selection; }; } function makeWildcardRecieptsSelection(wildcardLogDataRbs, nonLogDataWildcardReceiptTypes) { let selection = []; if (nonLogDataWildcardReceiptTypes.length !== 0) { selection.push({ receiptType: nonLogDataWildcardReceiptTypes, txStatus: txStatusSelection }); } if (wildcardLogDataRbs.length !== 0) { selection.push({ receiptType: logDataReceiptTypeSelection, rb: wildcardLogDataRbs, txStatus: txStatusSelection }); } return selection; } function getSelectionConfig(selection, chain) { let eventRouter = EventRouter.empty(); let nonWildcardLogDataRbsByContract = {}; let wildcardLogDataRbs = []; let nonLogDataReceiptTypesByContract = {}; let nonLogDataWildcardReceiptTypes = []; let addNonLogDataWildcardReceiptTypes = receiptType => { nonLogDataWildcardReceiptTypes.push(receiptType); }; let addNonLogDataReceiptType = (contractName, receiptType) => { let receiptTypes = nonLogDataReceiptTypesByContract[contractName]; if (receiptTypes !== undefined) { receiptTypes.push(receiptType); } else { nonLogDataReceiptTypesByContract[contractName] = [receiptType]; } }; let contractNames = new Set(); selection.eventConfigs.forEach(eventConfig => { let contractName = eventConfig.contractName; if (!eventConfig.isWildcard) { contractNames.add(contractName); } EventRouter.addOrThrow(eventRouter, eventConfig.id, eventConfig, contractName, eventConfig.isWildcard, eventConfig.name, chain); let isWildcard = eventConfig.isWildcard; if (isWildcard) { let tmp = eventConfig.kind; if (typeof tmp !== "object") { switch (tmp) { case "Mint" : return addNonLogDataWildcardReceiptTypes(11); case "Burn" : return addNonLogDataWildcardReceiptTypes(12); case "Transfer" : addNonLogDataWildcardReceiptTypes(7); return addNonLogDataWildcardReceiptTypes(8); case "Call" : return addNonLogDataWildcardReceiptTypes(0); } } } let match = eventConfig.kind; if (typeof match !== "object") { switch (match) { case "Mint" : return addNonLogDataReceiptType(contractName, 11); case "Burn" : return addNonLogDataReceiptType(contractName, 12); case "Transfer" : addNonLogDataReceiptType(contractName, 7); return addNonLogDataReceiptType(contractName, 8); case "Call" : return Stdlib_JsError.throwWithMessage("Call receipt indexing currently supported only in wildcard mode"); } } else { let rb = BigInt(match.logId); if (isWildcard) { wildcardLogDataRbs.push(rb); return; } let arr = nonWildcardLogDataRbsByContract[contractName]; if (arr !== undefined) { arr.push(rb); } else { nonWildcardLogDataRbsByContract[contractName] = [rb]; } return; } }); let tmp; if (selection.dependsOnAddresses) { tmp = makeGetNormalRecieptsSelection(nonWildcardLogDataRbsByContract, nonLogDataReceiptTypesByContract, contractNames); } else { let recieptsSelection = makeWildcardRecieptsSelection(wildcardLogDataRbs, nonLogDataWildcardReceiptTypes); tmp = param => recieptsSelection; } return { getRecieptsSelection: tmp, eventRouter: eventRouter }; } function memoGetSelectionConfig(chain) { let cache = new WeakMap(); return selection => { let c = cache.get(selection); if (c !== undefined) { return c; } let c$1 = getSelectionConfig(selection, chain); cache.set(selection, c$1); return c$1; }; } function make(param) { let endpointUrl = param.endpointUrl; let chain = param.chain; let name = "HyperFuel"; let getSelectionConfig = memoGetSelectionConfig(chain); let getItemsOrThrow = async (fromBlock, toBlock, addressesByContractName, indexingAddresses, knownHeight, param, selection, retry, logger) => { let totalTimeRef = Hrtime.makeTimer(); let selectionConfig = getSelectionConfig(selection); let recieptsSelection = selectionConfig.getRecieptsSelection(addressesByContractName); let startFetchingBatchTimeRef = Hrtime.makeTimer(); Prometheus.SourceRequestCount.increment(name, chain, "getLogs"); let pageUnsafe; try { pageUnsafe = await HyperFuel.GetLogs.query(endpointUrl, fromBlock, toBlock, recieptsSelection); } catch (raw_error) { let error = Primitive_exceptions.internalToException(raw_error); if (error.RE_EXN_ID === HyperSync.GetLogs.$$Error) { let error$1 = error._1; let tmp; if (typeof error$1 !== "object") { let backoffMillis = retry !== 0 ? 500 * retry | 0 : 100; tmp = { TAG: "WithBackoff", message: `Block #` + fromBlock.toString() + ` not found in HyperFuel. HyperFuel has multiple instances and it's possible that they drift independently slightly from the head. Indexing should continue correctly after retrying the query in ` + backoffMillis.toString() + `ms.`, backoffMillis: backoffMillis }; } else { tmp = { TAG: "ImpossibleForTheQuery", message: `Source returned invalid data with missing required fields: ` + error$1.missingParams.join(", ") }; } throw { RE_EXN_ID: Source.GetItemsError, _1: { TAG: "FailedGettingItems", exn: null, attemptedToBlock: Stdlib_Option.getOr(toBlock, knownHeight), retry: tmp }, Error: new Error() }; } throw { RE_EXN_ID: Source.GetItemsError, _1: { TAG: "FailedGettingItems", exn: error, attemptedToBlock: Stdlib_Option.getOr(toBlock, knownHeight), retry: { TAG: "WithBackoff", message: `Unexpected issue while fetching events from HyperFuel client. Attempt a retry.`, backoffMillis: retry !== 0 ? 1000 * retry | 0 : 500 } }, Error: new Error() }; } let pageFetchTime = Hrtime.toSecondsFloat(Hrtime.timeSince(startFetchingBatchTimeRef)); let knownHeight$1 = pageUnsafe.archiveHeight; let heighestBlockQueried = pageUnsafe.nextBlock - 1 | 0; let parsingTimeRef = Hrtime.makeTimer(); let parsedQueueItems = pageUnsafe.items.map(item => { let block = item.block; let receiptIndex = item.receiptIndex; let receipt = item.receipt; let contractAddress = item.contractId; let eventId; switch (receipt.receiptType) { case 0 : eventId = callEventTag; break; case 6 : eventId = receipt.rb.toString(); break; case 7 : case 8 : eventId = transferEventTag; break; case 11 : eventId = mintEventTag; break; case 12 : eventId = burnEventTag; break; } let eventConfig = EventRouter.get(selectionConfig.eventRouter, eventId, contractAddress, block.height, indexingAddresses); let eventConfig$1; if (eventConfig !== undefined) { eventConfig$1 = eventConfig; } else { let logger$1 = Logging.createChildFrom(logger, { chainId: chain, blockNumber: block.height, logIndex: receiptIndex, contractAddress: contractAddress, eventId: eventId }); eventConfig$1 = ErrorHandling.mkLogAndRaise(logger$1, "Failed to route registered event", { RE_EXN_ID: EventRoutingFailed }); } let match = eventConfig$1.kind; let params; let exit = 0; if (typeof match !== "object" || receipt.receiptType !== 6) { exit = 1; } else { try { params = match.decode(receipt.data); } catch (raw_exn) { let exn = Primitive_exceptions.internalToException(raw_exn); let params$1 = { chainId: chain, blockNumber: block.height, logIndex: receiptIndex }; let logger$2 = Logging.createChildFrom(logger, params$1); params = ErrorHandling.mkLogAndRaise(logger$2, "Failed to decode Fuel LogData receipt, please double check your ABI.", exn); } } if (exit === 1) { switch (receipt.receiptType) { case 0 : params = { to: receipt.to, assetId: receipt.assetId, amount: receipt.amount }; break; case 6 : params = Stdlib_JsError.throwWithMessage("Unexpected bug in the event routing logic"); break; case 7 : params = { to: receipt.to, assetId: receipt.assetId, amount: receipt.amount }; break; case 8 : params = { to: receipt.toAddress, assetId: receipt.assetId, amount: receipt.amount }; break; case 11 : case 12 : params = { subId: receipt.subId, amount: receipt.val }; break; } } return { kind: 0, eventConfig: eventConfig$1, timestamp: block.time, chain: chain, blockNumber: block.height, blockHash: block.id, logIndex: receiptIndex, event: { contractName: eventConfig$1.contractName, eventName: eventConfig$1.name, params: params, chainId: chain, srcAddress: contractAddress, logIndex: receiptIndex, transaction: { id: item.transactionId }, block: block } }; }); let parsingTimeElapsed = Hrtime.toSecondsFloat(Hrtime.timeSince(parsingTimeRef)); let blockHashes = pageUnsafe.items.map(param => { let block = param.block; return { blockHash: block.id, blockNumber: block.height }; }); let match = pageUnsafe.items[pageUnsafe.items.length - 1 | 0]; let latestFetchedBlockTimestamp; if (match !== undefined) { let block = match.block; latestFetchedBlockTimestamp = block.height === heighestBlockQueried ? block.time : 0; } else { latestFetchedBlockTimestamp = 0; } let totalTimeElapsed = Hrtime.toSecondsFloat(Hrtime.timeSince(totalTimeRef)); let stats_parsing$unknowntime$unknown$lpars$rpar = parsingTimeElapsed; let stats_page$unknownfetch$unknowntime$unknown$lpars$rpar = pageFetchTime; let stats = { "total time elapsed (s)": totalTimeElapsed, "parsing time (s)": stats_parsing$unknowntime$unknown$lpars$rpar, "page fetch time (s)": stats_page$unknownfetch$unknowntime$unknown$lpars$rpar }; return { knownHeight: knownHeight$1, blockHashes: blockHashes, parsedQueueItems: parsedQueueItems, fromBlockQueried: fromBlock, latestFetchedBlockNumber: heighestBlockQueried, latestFetchedBlockTimestamp: latestFetchedBlockTimestamp, stats: stats }; }; let getBlockHashes = (param, param$1) => Stdlib_JsError.throwWithMessage("HyperFuel does not support getting block hashes"); let jsonApiClient = EnvioApiClient.make(endpointUrl); return { name: name, sourceFor: "Sync", chain: chain, poweredByHyperSync: true, pollingInterval: 100, getBlockHashes: getBlockHashes, getHeightOrThrow: async () => { let timerRef = Hrtime.makeTimer(); let height = await Rest.fetch(HyperFuel.heightRoute, undefined, jsonApiClient); let seconds = Hrtime.toSecondsFloat(Hrtime.timeSince(timerRef)); Prometheus.SourceRequestCount.increment(name, chain, "getHeight"); Prometheus.SourceRequestCount.addSeconds(name, chain, "getHeight", seconds); return height; }, getItemsOrThrow: getItemsOrThrow }; } export { EventRoutingFailed, mintEventTag, burnEventTag, transferEventTag, callEventTag, logDataReceiptTypeSelection, txStatusSelection, makeGetNormalRecieptsSelection, makeWildcardRecieptsSelection, getSelectionConfig, memoGetSelectionConfig, make, } /* Rest Not a pure module */