envio
Version:
A latency and sync speed optimized, developer friendly blockchain data indexer.
315 lines (299 loc) • 14.2 kB
JavaScript
// 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 */