UNPKG

envio

Version:

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

315 lines (299 loc) • 14.2 kB
// Generated by ReScript, PLEASE EDIT WITH CARE import * as Env from "./Env.res.mjs"; import * as Svm from "./sources/Svm.res.mjs"; import * as Utils from "./Utils.res.mjs"; import * as Logging from "./Logging.res.mjs"; import * as ChainMap from "./ChainMap.res.mjs"; import * as EvmChain from "./sources/EvmChain.res.mjs"; import * as FetchState from "./FetchState.res.mjs"; import * as Prometheus from "./Prometheus.res.mjs"; import * as EventRouter from "./sources/EventRouter.res.mjs"; import * as UserContext from "./UserContext.res.mjs"; import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js"; import * as ErrorHandling from "./ErrorHandling.res.mjs"; import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js"; import * as SourceManager from "./sources/SourceManager.res.mjs"; import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js"; import * as ReorgDetection from "./ReorgDetection.res.mjs"; import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js"; import * as Stdlib_Promise from "@rescript/runtime/lib/es6/Stdlib_Promise.js"; import * as HyperFuelSource from "./sources/HyperFuelSource.res.mjs"; import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js"; import * as SvmHyperSyncSource from "./sources/SvmHyperSyncSource.res.mjs"; import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js"; import * as SafeCheckpointTracking from "./SafeCheckpointTracking.res.mjs"; function configAddresses(chainConfig) { let addresses = []; chainConfig.contracts.forEach(contract => { contract.addresses.forEach(address => { addresses.push({ address: address, contractName: contract.name, registrationBlock: -1 }); }); }); return addresses; } function make(chainConfig, indexingAddresses, startBlock, endBlock, firstEventBlockOpt, progressBlockNumber, config, registrations, targetBufferSize, logger, timestampCaughtUpToHeadOrEndblock, numEventsProcessed, isInReorgThreshold, isRealtime, reorgCheckpoints, maxReorgDepth, knownHeightOpt, reducedPollingInterval) { let firstEventBlock = firstEventBlockOpt !== undefined ? Primitive_option.valFromOption(firstEventBlockOpt) : undefined; let knownHeight = knownHeightOpt !== undefined ? knownHeightOpt : 0; let eventRouter = EventRouter.empty(); let eventConfigs = []; let notRegisteredEvents = []; chainConfig.contracts.forEach(contract => { let contractName = contract.name; contract.events.forEach(eventConfig => { let hasContractRegister = Stdlib_Option.isSome(eventConfig.contractRegister); EventRouter.addOrThrow(eventRouter, eventConfig.id, undefined, contractName, eventConfig.isWildcard, eventConfig.name, ChainMap.Chain.makeUnsafe(chainConfig.id)); let shouldBeIncluded; if (config.enableRawEvents) { shouldBeIncluded = true; } else { let isRegistered = hasContractRegister || Stdlib_Option.isSome(eventConfig.handler); if (!isRegistered) { notRegisteredEvents.push(eventConfig); } shouldBeIncluded = isRegistered; } let shouldSkip; try { let getEventFiltersOrThrow = eventConfig.getEventFiltersOrThrow; if (getEventFiltersOrThrow) { let match = getEventFiltersOrThrow(ChainMap.Chain.makeUnsafe(chainConfig.id)); shouldSkip = match.TAG === "Static" ? match._0.length === 0 : false; } else { shouldSkip = false; } } catch (exn) { shouldSkip = false; } if (shouldBeIncluded && !shouldSkip) { eventConfigs.push(eventConfig); return; } }); let startBlock = contract.startBlock; if (startBlock !== undefined && startBlock < chainConfig.startBlock) { return Stdlib_JsError.throwWithMessage(`The start block for contract "` + contractName + `" is less than the chain start block. This is not supported yet.`); } }); if (Utils.$$Array.notEmpty(notRegisteredEvents)) { Logging.childInfo(logger, `The event` + ( notRegisteredEvents.length > 1 ? "s" : "" ) + ` ` + notRegisteredEvents.map(eventConfig => eventConfig.contractName + `.` + eventConfig.name).join(", ") + ` don't have an event handler and skipped for indexing.`); } let onBlockConfigs = registrations.onBlockByChainId[chainConfig.id.toString()]; if (onBlockConfigs !== undefined) { onBlockConfigs.forEach(onBlockConfig => { if (Stdlib_Option.getOr(onBlockConfig.startBlock, startBlock) < startBlock) { Stdlib_JsError.throwWithMessage(`The start block for onBlock handler "` + onBlockConfig.name + `" is less than the chain start block (` + startBlock.toString() + `). This is not supported yet.`); } if (endBlock !== undefined && Stdlib_Option.getOr(onBlockConfig.endBlock, endBlock) > endBlock) { return Stdlib_JsError.throwWithMessage(`The end block for onBlock handler "` + onBlockConfig.name + `" is greater than the chain end block (` + endBlock.toString() + `). This is not supported yet.`); } }); } let fetchState = FetchState.make(startBlock, endBlock, eventConfigs, indexingAddresses, config.maxAddrInPartition, chainConfig.id, targetBufferSize, knownHeight, progressBlockNumber, onBlockConfigs, Primitive_int.max(!config.shouldRollbackOnReorg || isInReorgThreshold ? 0 : chainConfig.maxReorgDepth, chainConfig.blockLag), Primitive_option.some(firstEventBlock)); let chainReorgCheckpoints = Stdlib_Array.filterMap(reorgCheckpoints, reorgCheckpoint => { if (reorgCheckpoint.chain_id === chainConfig.id) { return reorgCheckpoint; } }); let chain = ChainMap.Chain.makeUnsafe(chainConfig.id); let lowercaseAddresses = config.lowercaseAddresses; let sources = chainConfig.sourceConfig; let sources$1; switch (sources.TAG) { case "EvmSourceConfig" : let evmContracts = chainConfig.contracts.map(contract => ({ name: contract.name, abi: contract.abi, events: contract.events })); let evmRpcs = sources.rpcs.map(rpc => { let syncConfig = rpc.syncConfig; let ws = rpc.ws; return { url: rpc.url, sourceFor: rpc.sourceFor, syncConfig: syncConfig, ws: ws }; }); sources$1 = EvmChain.makeSources(chain, evmContracts, sources.hypersync, evmRpcs, lowercaseAddresses); break; case "FuelSourceConfig" : sources$1 = [HyperFuelSource.make({ chain: chain, endpointUrl: sources.hypersync })]; break; case "SvmSourceConfig" : let rpc = sources.rpc; let hypersync = sources.hypersync; if (hypersync !== undefined) { let svmEventConfigs = chainConfig.contracts.flatMap(contract => contract.events); sources$1 = [SvmHyperSyncSource.make({ chain: chain, endpointUrl: hypersync, apiToken: Env.envioApiToken, eventConfigs: svmEventConfigs, clientTimeoutMillis: Env.hyperSyncClientTimeoutMillis })]; } else { sources$1 = rpc !== undefined ? [Svm.makeRPCSource(chain, rpc, undefined)] : Stdlib_JsError.throwWithMessage(`Chain ` + chain.toString() + ` has no SVM data source`); } break; case "CustomSources" : sources$1 = sources._0; break; } return { logger: logger, fetchState: fetchState, sourceManager: SourceManager.make(sources$1, Env.maxPartitionConcurrency, isRealtime, undefined, undefined, undefined, reducedPollingInterval, undefined, undefined), chainConfig: chainConfig, isProgressAtHead: false, timestampCaughtUpToHeadOrEndblock: timestampCaughtUpToHeadOrEndblock, committedProgressBlockNumber: progressBlockNumber, numEventsProcessed: numEventsProcessed, reorgDetection: ReorgDetection.make(chainReorgCheckpoints, maxReorgDepth, config.shouldRollbackOnReorg), safeCheckpointTracking: SafeCheckpointTracking.make(maxReorgDepth, config.shouldRollbackOnReorg, chainReorgCheckpoints) }; } function makeFromConfig(chainConfig, config, registrations, targetBufferSize, knownHeight) { let logger = Logging.createChild({ chainId: chainConfig.id }); return make(chainConfig, configAddresses(chainConfig), chainConfig.startBlock, chainConfig.endBlock, undefined, -1, config, registrations, targetBufferSize, logger, undefined, 0, false, false, [], chainConfig.maxReorgDepth, knownHeight, undefined); } function makeFromDbState(chainConfig, resumedChainState, reorgCheckpoints, isInReorgThreshold, isRealtime, config, registrations, targetBufferSize, reducedPollingInterval) { let chainId = chainConfig.id; let logger = Logging.createChild({ chainId: chainId }); Prometheus.ProgressEventsCount.set(resumedChainState.numEventsProcessed, chainId); let progressBlockNumber = resumedChainState.progressBlockNumber >= 0 ? resumedChainState.progressBlockNumber : resumedChainState.startBlock - 1 | 0; return make(chainConfig, resumedChainState.indexingAddresses, resumedChainState.startBlock, resumedChainState.endBlock, Primitive_option.some(resumedChainState.firstEventBlockNumber), progressBlockNumber, config, registrations, targetBufferSize, logger, Env.updateSyncTimeOnRestart ? undefined : resumedChainState.timestampCaughtUpToHeadOrEndblock, resumedChainState.numEventsProcessed, isInReorgThreshold, isRealtime, reorgCheckpoints, resumedChainState.maxReorgDepth, resumedChainState.sourceBlockNumber, reducedPollingInterval); } async function runContractRegistersOrThrow(itemsWithContractRegister, config) { let itemsWithDcs = []; let onRegister = (item, contractAddress, contractName) => { let dc_registrationBlock = item.blockNumber; let dc = { address: contractAddress, contractName: contractName, registrationBlock: dc_registrationBlock }; let dcs = item.dcs; if (dcs !== undefined) { dcs.push(dc); } else { item.dcs = [dc]; itemsWithDcs.push(item); } }; let promises = []; for (let idx = 0, idx_finish = itemsWithContractRegister.length; idx < idx_finish; ++idx) { let item = itemsWithContractRegister[idx]; let match = item.eventConfig; let contractRegister = match.contractRegister; let contractRegister$1 = contractRegister !== undefined ? contractRegister : Stdlib_JsError.throwWithMessage("Contract register is not set for event " + match.name); let errorMessage = "Event contractRegister failed, please fix the error to keep the indexer running smoothly"; try { let params = { item: item, onRegister: onRegister, config: config, isResolved: false }; let result = contractRegister$1(UserContext.getContractRegisterArgs(params)); if (Utils.$$Promise.isCatchable(result)) { promises.push(Stdlib_Promise.$$catch(result.then(r => { params.isResolved = true; }), exn => { params.isResolved = true; return ErrorHandling.mkLogAndRaise(Logging.getItemLogger(item), errorMessage, exn); })); } else { params.isResolved = true; } } catch (raw_exn) { let exn = Primitive_exceptions.internalToException(raw_exn); ErrorHandling.mkLogAndRaise(Logging.getItemLogger(item), errorMessage, exn); } } if (Utils.$$Array.notEmpty(promises)) { await Promise.all(promises); } return itemsWithDcs; } function handleQueryResult(chainFetcher, query, newItems, newItemsWithDcs, latestFetchedBlock, knownHeight) { let fs = newItemsWithDcs.length !== 0 ? FetchState.registerDynamicContracts(chainFetcher.fetchState, newItemsWithDcs) : chainFetcher.fetchState; return { logger: chainFetcher.logger, fetchState: FetchState.updateKnownHeight(FetchState.handleQueryResult(fs, query, latestFetchedBlock, newItems), knownHeight), sourceManager: chainFetcher.sourceManager, chainConfig: chainFetcher.chainConfig, isProgressAtHead: chainFetcher.isProgressAtHead, timestampCaughtUpToHeadOrEndblock: chainFetcher.timestampCaughtUpToHeadOrEndblock, committedProgressBlockNumber: chainFetcher.committedProgressBlockNumber, numEventsProcessed: chainFetcher.numEventsProcessed, reorgDetection: chainFetcher.reorgDetection, safeCheckpointTracking: chainFetcher.safeCheckpointTracking }; } function hasProcessedToEndblock(self) { let endBlock = self.fetchState.endBlock; if (endBlock !== undefined) { return self.committedProgressBlockNumber >= endBlock; } else { return false; } } function hasNoMoreEventsToProcess(self) { return FetchState.bufferSize(self.fetchState) === 0; } function getHighestBlockBelowThreshold(cf) { let highestBlockBelowThreshold = cf.fetchState.knownHeight - cf.chainConfig.maxReorgDepth | 0; if (highestBlockBelowThreshold < 0) { return 0; } else { return highestBlockBelowThreshold; } } async function getLastKnownValidBlock(chainFetcher, reorgBlockNumber, isRealtime) { let scannedBlockNumbers = ReorgDetection.getThresholdBlockNumbersBelowBlock(chainFetcher.reorgDetection, reorgBlockNumber, chainFetcher.fetchState.knownHeight); if (scannedBlockNumbers.length === 0) { return getHighestBlockBelowThreshold(chainFetcher); } let blockNumbersAndHashes = await SourceManager.getBlockHashes(chainFetcher.sourceManager, scannedBlockNumbers, isRealtime); let blockNumber = ReorgDetection.getLatestValidScannedBlock(chainFetcher.reorgDetection, blockNumbersAndHashes); if (blockNumber !== undefined) { return blockNumber; } else { return getHighestBlockBelowThreshold(chainFetcher); } } function isActivelyIndexing(chainFetcher) { return FetchState.isActivelyIndexing(chainFetcher.fetchState); } function isReady(chainFetcher) { return chainFetcher.timestampCaughtUpToHeadOrEndblock !== undefined; } export { configAddresses, make, makeFromConfig, makeFromDbState, runContractRegistersOrThrow, handleQueryResult, hasProcessedToEndblock, hasNoMoreEventsToProcess, getHighestBlockBelowThreshold, getLastKnownValidBlock, isActivelyIndexing, isReady, } /* Env Not a pure module */