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