envio
Version:
A latency and sync speed optimized, developer friendly blockchain data indexer.
1,260 lines (1,220 loc) • 43 kB
JavaScript
// Generated by ReScript, PLEASE EDIT WITH CARE
import * as Rpc from "./Rpc.res.mjs";
import * as Rest from "../vendored/Rest.res.mjs";
import * as Time from "../Time.res.mjs";
import * as Utils from "../Utils.res.mjs";
import * as Hrtime from "../bindings/Hrtime.res.mjs";
import * as Source from "./Source.res.mjs";
import * as Address from "../Address.res.mjs";
import * as Logging from "../Logging.res.mjs";
import * as Internal from "../Internal.res.mjs";
import * as FetchState from "../FetchState.res.mjs";
import * as LazyLoader from "../LazyLoader.res.mjs";
import * as Prometheus from "../Prometheus.res.mjs";
import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
import * as EventRouter from "./EventRouter.res.mjs";
import * as EvmRpcClient from "./EvmRpcClient.res.mjs";
import * as LogSelection from "../LogSelection.res.mjs";
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
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 HyperSyncClient from "./HyperSyncClient.res.mjs";
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
import * as RpcWebSocketHeightStream from "./RpcWebSocketHeightStream.res.mjs";
let QueryTimout = /* @__PURE__ */Primitive_exceptions.create("RpcSource.QueryTimout");
let TransactionDataNotFound = /* @__PURE__ */Primitive_exceptions.create("RpcSource.TransactionDataNotFound");
async function getKnownRawBlock(client, blockNumber) {
let json = await Rpc.getRawBlock(client, blockNumber);
if (json !== undefined) {
return json;
} else {
return Stdlib_JsError.throwWithMessage(`RPC returned null for blockNumber ` + blockNumber.toString());
}
}
function parseBlockInfo(json) {
return {
number: S$RescriptSchema.parseOrThrow(json["number"], Rpc.hexIntSchema),
timestamp: S$RescriptSchema.parseOrThrow(json["timestamp"], Rpc.hexIntSchema),
hash: S$RescriptSchema.parseOrThrow(json["hash"], S$RescriptSchema.string),
parentHash: S$RescriptSchema.parseOrThrow(json["parentHash"], S$RescriptSchema.string)
};
}
async function getKnownRawBlockWithBackoff(client, sourceName, chain, blockNumber, backoffMsOnFailure) {
let currentBackoff = backoffMsOnFailure;
let result;
while (Stdlib_Option.isNone(result)) {
Prometheus.SourceRequestCount.increment(sourceName, chain, "eth_getBlockByNumber");
let exit = 0;
let json;
try {
json = await getKnownRawBlock(client, blockNumber);
exit = 1;
} catch (raw_err) {
let err = Primitive_exceptions.internalToException(raw_err);
Logging.warn({
err: Utils.prettifyExn(err),
msg: `Issue while running fetching batch of events from the RPC. Will wait ` + currentBackoff.toString() + `ms and try again.`,
source: sourceName,
chainId: chain,
type: "EXPONENTIAL_BACKOFF"
});
await Time.resolvePromiseAfterDelay(currentBackoff);
currentBackoff = (currentBackoff << 1);
}
if (exit === 1) {
result = json;
}
};
return Stdlib_Option.getOrThrow(result, undefined);
}
let suggestedRangeRegExp = /retry with the range (\d+)-(\d+)/;
let blockRangeLimitRegExp = /limited to a (\d+) blocks range/;
let alchemyRangeRegExp = /up to a (\d+) block range/;
let cloudflareRangeRegExp = /Max range: (\d+)/;
let thirdwebRangeRegExp = /Maximum allowed number of requested blocks is (\d+)/;
let blockpiRangeRegExp = /limited to (\d+) block/;
let baseRangeRegExp = /block range too large/;
let maxAllowedBlocksRegExp = /maximum allowed is (\d+) blocks/;
let blastPaidRegExp = /exceeds the range allowed for your plan \(\d+ > (\d+)\)/;
let chainstackRegExp = /Block range limit exceeded./;
let coinbaseRegExp = /please limit the query to at most (\d+) blocks/;
let publicNodeRegExp = /maximum block range: (\d+)/;
let hyperliquidRegExp = /query exceeds max block range (\d+)/;
function parseMessageForBlockRange(message) {
let extractBlockRange = (execResult, isMaxRange) => {
let match = execResult.slice(1);
if (match.length !== 1) {
return;
}
let blockRangeLimit = match[0];
if (blockRangeLimit === undefined) {
return;
}
let blockRangeLimit$1 = Stdlib_Int.fromString(blockRangeLimit, undefined);
if (blockRangeLimit$1 !== undefined && blockRangeLimit$1 > 0) {
return [
blockRangeLimit$1,
isMaxRange
];
}
};
let execResult = suggestedRangeRegExp.exec(message);
if (execResult == null) {
let execResult$1 = blockRangeLimitRegExp.exec(message);
if (!(execResult$1 == null)) {
return extractBlockRange(execResult$1, true);
}
let execResult$2 = alchemyRangeRegExp.exec(message);
if (!(execResult$2 == null)) {
return extractBlockRange(execResult$2, true);
}
let execResult$3 = cloudflareRangeRegExp.exec(message);
if (!(execResult$3 == null)) {
return extractBlockRange(execResult$3, true);
}
let execResult$4 = thirdwebRangeRegExp.exec(message);
if (!(execResult$4 == null)) {
return extractBlockRange(execResult$4, true);
}
let execResult$5 = blockpiRangeRegExp.exec(message);
if (!(execResult$5 == null)) {
return extractBlockRange(execResult$5, true);
}
let execResult$6 = maxAllowedBlocksRegExp.exec(message);
if (!(execResult$6 == null)) {
return extractBlockRange(execResult$6, true);
}
let match = baseRangeRegExp.exec(message);
if (!(match == null)) {
return [
2000,
true
];
}
let execResult$7 = blastPaidRegExp.exec(message);
if (!(execResult$7 == null)) {
return extractBlockRange(execResult$7, true);
}
let match$1 = chainstackRegExp.exec(message);
if (!(match$1 == null)) {
return [
10000,
true
];
}
let execResult$8 = coinbaseRegExp.exec(message);
if (!(execResult$8 == null)) {
return extractBlockRange(execResult$8, true);
}
let execResult$9 = publicNodeRegExp.exec(message);
if (!(execResult$9 == null)) {
return extractBlockRange(execResult$9, true);
}
let execResult$10 = hyperliquidRegExp.exec(message);
if (!(execResult$10 == null)) {
return extractBlockRange(execResult$10, true);
} else {
return;
}
}
let match$2 = execResult.slice(1);
if (match$2.length !== 2) {
return;
}
let fromBlock = match$2[0];
if (fromBlock === undefined) {
return;
}
let toBlock = match$2[1];
if (toBlock === undefined) {
return;
}
let match$3 = Stdlib_Int.fromString(fromBlock, undefined);
let match$4 = Stdlib_Int.fromString(toBlock, undefined);
if (match$3 !== undefined && match$4 !== undefined && match$4 >= match$3) {
return [
(match$4 - match$3 | 0) + 1 | 0,
false
];
}
}
function getSuggestedBlockIntervalFromExn(exn) {
if (exn.RE_EXN_ID === Rpc.JsonRpcError) {
return parseMessageForBlockRange(exn._1.message);
}
if (exn.RE_EXN_ID !== "JsExn") {
return;
}
try {
let message = exn._1.error.message;
S$RescriptSchema.assertOrThrow(message, S$RescriptSchema.string);
return parseMessageForBlockRange(message);
} catch (exn$1) {
return;
}
}
let maxSuggestedBlockIntervalKey = "max";
function getNextPage(fromBlock, toBlock, addresses, topicQuery, loadBlock, sc, client, mutSuggestedBlockIntervals, partitionId, sourceName, chainId) {
let queryTimoutPromise = Time.resolvePromiseAfterDelay(sc.queryTimeoutMillis).then(() => Promise.reject({
RE_EXN_ID: QueryTimout,
_1: `Query took longer than ` + (sc.queryTimeoutMillis / 1000 | 0).toString() + ` seconds`
}));
let latestFetchedBlockPromise = loadBlock(toBlock);
Prometheus.SourceRequestCount.increment(sourceName, chainId, "eth_getLogs");
let logsPromise = Rpc.getLogs(client, {
fromBlock: fromBlock,
toBlock: toBlock,
address: addresses,
topics: topicQuery
}).then(async logs => ({
logs: logs,
latestFetchedBlockInfo: await latestFetchedBlockPromise
}));
return Stdlib_Promise.$$catch(Promise.race([
queryTimoutPromise,
logsPromise
]), err => {
let match = getSuggestedBlockIntervalFromExn(err);
if (match !== undefined) {
let nextBlockIntervalTry = match[0];
mutSuggestedBlockIntervals[match[1] ? maxSuggestedBlockIntervalKey : partitionId] = nextBlockIntervalTry;
throw {
RE_EXN_ID: Source.GetItemsError,
_1: {
TAG: "FailedGettingItems",
exn: err,
attemptedToBlock: toBlock,
retry: {
TAG: "WithSuggestedToBlock",
toBlock: (fromBlock + nextBlockIntervalTry | 0) - 1 | 0
}
},
Error: new Error()
};
}
let executedBlockInterval = (toBlock - fromBlock | 0) + 1 | 0;
let nextBlockIntervalTry$1 = executedBlockInterval * sc.backoffMultiplicative | 0;
mutSuggestedBlockIntervals[partitionId] = nextBlockIntervalTry$1;
throw {
RE_EXN_ID: Source.GetItemsError,
_1: {
TAG: "FailedGettingItems",
exn: err,
attemptedToBlock: toBlock,
retry: {
TAG: "WithBackoff",
message: `Failed getting data for the block range. Will try smaller block range for the next attempt.`,
backoffMillis: sc.backoffMillis
}
},
Error: new Error()
};
});
}
function getSelectionConfig(selection, chain) {
let staticTopicSelections = [];
let dynamicEventFilters = [];
selection.eventConfigs.forEach(param => {
let s = param.getEventFiltersOrThrow(chain);
if (s.TAG === "Static") {
staticTopicSelections.push(...s._0);
return;
}
dynamicEventFilters.push(s._0);
});
let match = LogSelection.compressTopicSelections(staticTopicSelections);
let len = match.length;
let getLogSelectionOrThrow;
if (len !== 1) {
if (len !== 0) {
throw {
RE_EXN_ID: Source.GetItemsError,
_1: {
TAG: "UnsupportedSelection",
message: "RPC data-source currently supports event filters only when there's a single wildcard event. Please, create a GitHub issue if it's a blocker for you."
},
Error: new Error()
};
}
let len$1 = dynamicEventFilters.length;
if (len$1 !== 1) {
if (len$1 !== 0) {
throw {
RE_EXN_ID: Source.GetItemsError,
_1: {
TAG: "UnsupportedSelection",
message: "RPC data-source currently supports event filters only when there's a single wildcard event. Please, create a GitHub issue if it's a blocker for you."
},
Error: new Error()
};
}
throw {
RE_EXN_ID: Source.GetItemsError,
_1: {
TAG: "UnsupportedSelection",
message: "Invalid events configuration for the partition. Nothing to fetch. Please, report to the Envio team."
},
Error: new Error()
};
}
let dynamicEventFilter = dynamicEventFilters[0];
if (selection.eventConfigs.length === 1) {
let eventConfig = Utils.$$Array.firstUnsafe(selection.eventConfigs);
getLogSelectionOrThrow = addressesByContractName => {
let addresses = FetchState.addressesByContractNameGetAll(addressesByContractName);
let match = dynamicEventFilter(addresses);
if (match.length !== 1) {
throw {
RE_EXN_ID: Source.GetItemsError,
_1: {
TAG: "UnsupportedSelection",
message: "RPC data-source currently doesn't support an array of event filters. Please, create a GitHub issue if it's a blocker for you."
},
Error: new Error()
};
}
return {
addresses: eventConfig.isWildcard ? undefined : addresses,
topicQuery: Rpc.GetLogs.mapTopicQuery(match[0])
};
};
} else {
throw {
RE_EXN_ID: Source.GetItemsError,
_1: {
TAG: "UnsupportedSelection",
message: "RPC data-source currently supports event filters only when there's a single wildcard event. Please, create a GitHub issue if it's a blocker for you."
},
Error: new Error()
};
}
} else {
let topicSelection = match[0];
if (dynamicEventFilters.length !== 0) {
throw {
RE_EXN_ID: Source.GetItemsError,
_1: {
TAG: "UnsupportedSelection",
message: "RPC data-source currently supports event filters only when there's a single wildcard event. Please, create a GitHub issue if it's a blocker for you."
},
Error: new Error()
};
}
let topicQuery = Rpc.GetLogs.mapTopicQuery(topicSelection);
getLogSelectionOrThrow = addressesByContractName => {
let addresses = FetchState.addressesByContractNameGetAll(addressesByContractName);
return {
addresses: addresses.length !== 0 ? addresses : undefined,
topicQuery: topicQuery
};
};
}
return {
getLogSelectionOrThrow: getLogSelectionOrThrow
};
}
function memoGetSelectionConfig(chain) {
return Utils.$$WeakMap.memoize(selection => getSelectionConfig(selection, chain));
}
let lowercaseAddressSchema = S$RescriptSchema.transform(S$RescriptSchema.string, param => ({
p: str => str.toLowerCase()
}));
let checksumAddressSchema = S$RescriptSchema.transform(S$RescriptSchema.string, param => ({
p: str => Address.Evm.fromStringOrThrow(str)
}));
function makeBlockFieldRegistry(addressSchema) {
return Utils.Record.fromArray([
{
location: "number",
jsonKey: "number",
schema: Rpc.hexIntSchema
},
{
location: "timestamp",
jsonKey: "timestamp",
schema: Rpc.hexIntSchema
},
{
location: "hash",
jsonKey: "hash",
schema: S$RescriptSchema.string
},
{
location: "parentHash",
jsonKey: "parentHash",
schema: S$RescriptSchema.string
},
{
location: "nonce",
jsonKey: "nonce",
schema: Rpc.hexBigintSchema
},
{
location: "sha3Uncles",
jsonKey: "sha3Uncles",
schema: S$RescriptSchema.string
},
{
location: "logsBloom",
jsonKey: "logsBloom",
schema: S$RescriptSchema.string
},
{
location: "transactionsRoot",
jsonKey: "transactionsRoot",
schema: S$RescriptSchema.string
},
{
location: "stateRoot",
jsonKey: "stateRoot",
schema: S$RescriptSchema.string
},
{
location: "receiptsRoot",
jsonKey: "receiptsRoot",
schema: S$RescriptSchema.string
},
{
location: "miner",
jsonKey: "miner",
schema: addressSchema
},
{
location: "difficulty",
jsonKey: "difficulty",
schema: Rpc.hexBigintSchema
},
{
location: "totalDifficulty",
jsonKey: "totalDifficulty",
schema: Rpc.hexBigintSchema
},
{
location: "extraData",
jsonKey: "extraData",
schema: S$RescriptSchema.string
},
{
location: "size",
jsonKey: "size",
schema: Rpc.hexBigintSchema
},
{
location: "gasLimit",
jsonKey: "gasLimit",
schema: Rpc.hexBigintSchema
},
{
location: "gasUsed",
jsonKey: "gasUsed",
schema: Rpc.hexBigintSchema
},
{
location: "uncles",
jsonKey: "uncles",
schema: S$RescriptSchema.array(S$RescriptSchema.string)
},
{
location: "baseFeePerGas",
jsonKey: "baseFeePerGas",
schema: Rpc.hexBigintSchema
},
{
location: "blobGasUsed",
jsonKey: "blobGasUsed",
schema: Rpc.hexBigintSchema
},
{
location: "excessBlobGas",
jsonKey: "excessBlobGas",
schema: Rpc.hexBigintSchema
},
{
location: "parentBeaconBlockRoot",
jsonKey: "parentBeaconBlockRoot",
schema: S$RescriptSchema.string
},
{
location: "withdrawalsRoot",
jsonKey: "withdrawalsRoot",
schema: S$RescriptSchema.string
},
{
location: "l1BlockNumber",
jsonKey: "l1BlockNumber",
schema: Rpc.hexIntSchema
},
{
location: "sendCount",
jsonKey: "sendCount",
schema: S$RescriptSchema.string
},
{
location: "sendRoot",
jsonKey: "sendRoot",
schema: S$RescriptSchema.string
},
{
location: "mixHash",
jsonKey: "mixHash",
schema: S$RescriptSchema.string
}
].map(def => [
def.location,
Internal.evmNullableBlockFields.has(def.location) ? ({
location: def.location,
jsonKey: def.jsonKey,
schema: S$RescriptSchema.nullable(def.schema)
}) : def
]));
}
let blockFieldRegistryLowercase = makeBlockFieldRegistry(lowercaseAddressSchema);
let blockFieldRegistryChecksum = makeBlockFieldRegistry(checksumAddressSchema);
function parseBlockFieldsFromJson(mutBlockAcc, fields, json) {
fields.forEach(def => {
let raw = json[def.jsonKey];
try {
let parsed = S$RescriptSchema.parseOrThrow(raw, def.schema);
mutBlockAcc[def.location] = parsed;
return;
} catch (raw_error) {
let error = Primitive_exceptions.internalToException(raw_error);
if (error.RE_EXN_ID === S$RescriptSchema.Raised) {
return Stdlib_JsError.throwWithMessage(`Invalid block field "` + def.location + `" found in the RPC response. Error: ` + S$RescriptSchema.$$Error.reason(error._1));
}
throw error;
}
});
}
function makeThrowingGetEventBlock(getBlockJson, lowercaseAddresses) {
let blockFieldRegistry = lowercaseAddresses ? blockFieldRegistryLowercase : blockFieldRegistryChecksum;
let fnsCache = new WeakMap();
return (log, selectedBlockFields) => {
let fn = fnsCache.get(selectedBlockFields);
let tmp;
if (fn !== undefined) {
tmp = fn;
} else {
let fields = [];
selectedBlockFields.forEach(fieldName => {
fields.push(blockFieldRegistry[fieldName]);
});
let fn$1 = selectedBlockFields.size === 0 ? param => Promise.resolve({}) : log => getBlockJson(log.blockNumber).then(json => {
let mutBlockAcc = {};
parseBlockFieldsFromJson(mutBlockAcc, fields, json);
return mutBlockAcc;
});
fnsCache.set(selectedBlockFields, fn$1);
tmp = fn$1;
}
return tmp(log);
};
}
function makeFieldRegistry(addressSchema) {
return Utils.Record.fromArray([
{
location: "gas",
jsonKey: "gas",
schema: Rpc.hexBigintSchema,
source: "TransactionOnly"
},
{
location: "gasPrice",
jsonKey: "gasPrice",
schema: Rpc.hexBigintSchema,
source: "TransactionOnly"
},
{
location: "input",
jsonKey: "input",
schema: S$RescriptSchema.string,
source: "TransactionOnly"
},
{
location: "nonce",
jsonKey: "nonce",
schema: Rpc.hexBigintSchema,
source: "TransactionOnly"
},
{
location: "value",
jsonKey: "value",
schema: Rpc.hexBigintSchema,
source: "TransactionOnly"
},
{
location: "v",
jsonKey: "v",
schema: S$RescriptSchema.string,
source: "TransactionOnly"
},
{
location: "r",
jsonKey: "r",
schema: S$RescriptSchema.string,
source: "TransactionOnly"
},
{
location: "s",
jsonKey: "s",
schema: S$RescriptSchema.string,
source: "TransactionOnly"
},
{
location: "yParity",
jsonKey: "yParity",
schema: S$RescriptSchema.string,
source: "TransactionOnly"
},
{
location: "maxPriorityFeePerGas",
jsonKey: "maxPriorityFeePerGas",
schema: Rpc.hexBigintSchema,
source: "TransactionOnly"
},
{
location: "maxFeePerGas",
jsonKey: "maxFeePerGas",
schema: Rpc.hexBigintSchema,
source: "TransactionOnly"
},
{
location: "maxFeePerBlobGas",
jsonKey: "maxFeePerBlobGas",
schema: Rpc.hexBigintSchema,
source: "TransactionOnly"
},
{
location: "blobVersionedHashes",
jsonKey: "blobVersionedHashes",
schema: S$RescriptSchema.array(S$RescriptSchema.string),
source: "TransactionOnly"
},
{
location: "gasUsed",
jsonKey: "gasUsed",
schema: Rpc.hexBigintSchema,
source: "ReceiptOnly"
},
{
location: "cumulativeGasUsed",
jsonKey: "cumulativeGasUsed",
schema: Rpc.hexBigintSchema,
source: "ReceiptOnly"
},
{
location: "effectiveGasPrice",
jsonKey: "effectiveGasPrice",
schema: Rpc.hexBigintSchema,
source: "ReceiptOnly"
},
{
location: "contractAddress",
jsonKey: "contractAddress",
schema: addressSchema,
source: "ReceiptOnly"
},
{
location: "logsBloom",
jsonKey: "logsBloom",
schema: S$RescriptSchema.string,
source: "ReceiptOnly"
},
{
location: "root",
jsonKey: "root",
schema: S$RescriptSchema.string,
source: "ReceiptOnly"
},
{
location: "status",
jsonKey: "status",
schema: Rpc.hexIntSchema,
source: "ReceiptOnly"
},
{
location: "l1Fee",
jsonKey: "l1Fee",
schema: Rpc.hexBigintSchema,
source: "ReceiptOnly"
},
{
location: "l1GasPrice",
jsonKey: "l1GasPrice",
schema: Rpc.hexBigintSchema,
source: "ReceiptOnly"
},
{
location: "l1GasUsed",
jsonKey: "l1GasUsed",
schema: Rpc.hexBigintSchema,
source: "ReceiptOnly"
},
{
location: "l1FeeScalar",
jsonKey: "l1FeeScalar",
schema: Rpc.decimalFloatSchema,
source: "ReceiptOnly"
},
{
location: "gasUsedForL1",
jsonKey: "gasUsedForL1",
schema: Rpc.hexBigintSchema,
source: "ReceiptOnly"
},
{
location: "from",
jsonKey: "from",
schema: addressSchema,
source: "Both"
},
{
location: "to",
jsonKey: "to",
schema: addressSchema,
source: "Both"
},
{
location: "type",
jsonKey: "type",
schema: Rpc.hexIntSchema,
source: "Both"
}
].map(def => [
def.location,
Internal.evmNullableTransactionFields.has(def.location) ? ({
location: def.location,
jsonKey: def.jsonKey,
schema: S$RescriptSchema.nullable(def.schema),
source: def.source
}) : def
]));
}
let fieldRegistryLowercase = makeFieldRegistry(lowercaseAddressSchema);
let fieldRegistryChecksum = makeFieldRegistry(checksumAddressSchema);
function parseFieldsFromJson(mutTransactionAcc, fields, json) {
fields.forEach(def => {
let raw = json[def.jsonKey];
try {
let parsed = S$RescriptSchema.parseOrThrow(raw, def.schema);
mutTransactionAcc[def.location] = parsed;
return;
} catch (raw_error) {
let error = Primitive_exceptions.internalToException(raw_error);
if (error.RE_EXN_ID === S$RescriptSchema.Raised) {
return Stdlib_JsError.throwWithMessage(`Invalid transaction field "` + def.location + `" found in the RPC response. Error: ` + S$RescriptSchema.$$Error.reason(error._1));
}
throw error;
}
});
}
function makeThrowingGetEventTransaction(getTransactionJson, getReceiptJson, lowercaseAddresses) {
let fieldRegistry = lowercaseAddresses ? fieldRegistryLowercase : fieldRegistryChecksum;
let fnsCache = new WeakMap();
return (log, selectedTransactionFields) => {
let fn = fnsCache.get(selectedTransactionFields);
let tmp;
if (fn !== undefined) {
tmp = fn;
} else {
let hasTransactionIndex = {
contents: false
};
let hasHash = {
contents: false
};
let txFields = [];
let receiptFields = [];
let bothFields = [];
selectedTransactionFields.forEach(fieldName => {
switch (fieldName) {
case "transactionIndex" :
hasTransactionIndex.contents = true;
return;
case "hash" :
hasHash.contents = true;
return;
default:
let def = fieldRegistry[fieldName];
if (def === undefined) {
return;
}
let match = def.source;
switch (match) {
case "TransactionOnly" :
txFields.push(def);
return;
case "ReceiptOnly" :
receiptFields.push(def);
return;
case "Both" :
bothFields.push(def);
return;
}
}
});
let match = txFields.length !== 0;
let match$1 = receiptFields.length !== 0;
let strategy = match ? (
match$1 ? "TransactionAndReceipt" : "TransactionOnly"
) : (
match$1 ? "ReceiptOnly" : (
bothFields.length !== 0 ? "TransactionOnly" : "NoRpc"
)
);
let targetForBoth = strategy === "ReceiptOnly" ? receiptFields : txFields;
bothFields.forEach(f => {
targetForBoth.push(f);
});
let setLogFields = (mutTransactionAcc, log) => {
if (hasTransactionIndex.contents) {
mutTransactionAcc["transactionIndex"] = log.transactionIndex;
}
if (hasHash.contents) {
mutTransactionAcc["hash"] = log.transactionHash;
return;
}
};
let fn$1 = selectedTransactionFields.size === 0 ? param => Promise.resolve({}) : (
strategy === "NoRpc" ? log => {
let mutTransactionAcc = {};
setLogFields(mutTransactionAcc, log);
return Promise.resolve(mutTransactionAcc);
} : log => {
let txJsonPromise;
let exit = 0;
switch (strategy) {
case "NoRpc" :
case "ReceiptOnly" :
txJsonPromise = Promise.resolve(undefined);
break;
case "TransactionOnly" :
case "TransactionAndReceipt" :
exit = 1;
break;
}
if (exit === 1) {
txJsonPromise = getTransactionJson(log.transactionHash).then(v => v);
}
let receiptJsonPromise;
let exit$1 = 0;
switch (strategy) {
case "NoRpc" :
case "TransactionOnly" :
receiptJsonPromise = Promise.resolve(undefined);
break;
case "ReceiptOnly" :
case "TransactionAndReceipt" :
exit$1 = 1;
break;
}
if (exit$1 === 1) {
receiptJsonPromise = getReceiptJson(log.transactionHash).then(v => v);
}
return Promise.all([
txJsonPromise,
receiptJsonPromise
]).then(param => {
let receiptJson = param[1];
let txJson = param[0];
let mutTransactionAcc = {};
setLogFields(mutTransactionAcc, log);
if (txJson !== undefined) {
parseFieldsFromJson(mutTransactionAcc, txFields, txJson);
}
if (receiptJson !== undefined) {
parseFieldsFromJson(mutTransactionAcc, receiptFields, receiptJson);
}
return mutTransactionAcc;
});
}
);
fnsCache.set(selectedTransactionFields, fn$1);
tmp = fn$1;
}
return tmp(log);
};
}
function make(param) {
let lowercaseAddresses = param.lowercaseAddresses;
let allEventParams = param.allEventParams;
let eventRouter = param.eventRouter;
let chain = param.chain;
let url = param.url;
let syncConfig = param.syncConfig;
let host = Utils.Url.getHostFromUrl(url);
let urlHost = host !== undefined ? host : Stdlib_JsError.throwWithMessage(`The RPC url for chain ` + chain.toString() + ` is in incorrect format. The RPC url needs to start with either http:// or https://`);
let name = `RPC (` + urlHost + `)`;
let getSelectionConfig = memoGetSelectionConfig(chain);
let mutSuggestedBlockIntervals = {};
let client = Rpc.makeClient(url);
let rpcClient = EvmRpcClient.make(url, undefined);
let makeTransactionLoader = () => LazyLoader.make(transactionHash => {
Prometheus.SourceRequestCount.increment(name, chain, "eth_getTransactionByHash");
return Rest.fetch(Rpc.GetTransactionByHash.rawRoute, transactionHash, client);
}, (am, exn) => Logging.error({
err: Utils.prettifyExn(exn),
msg: `Top level promise timeout reached. Please review other errors or warnings in the code. This function will retry in ` + (am._retryDelayMillis / 1000 | 0).toString() + ` seconds. It is highly likely that your indexer isn't syncing on one or more chains currently. Also take a look at the "suggestedFix" in the metadata of this command`,
source: name,
chainId: chain,
metadata: {
asyncTaskName: "transactionLoader: fetching transaction data - `getTransaction` rpc call",
suggestedFix: "This likely means the RPC url you are using is not responding correctly. Please try another RPC endipoint."
}
}), undefined, undefined, undefined, undefined);
let makeBlockLoader = () => LazyLoader.make(blockNumber => getKnownRawBlockWithBackoff(client, name, chain, blockNumber, 1000), (am, exn) => Logging.error({
err: Utils.prettifyExn(exn),
msg: `Top level promise timeout reached. Please review other errors or warnings in the code. This function will retry in ` + (am._retryDelayMillis / 1000 | 0).toString() + ` seconds. It is highly likely that your indexer isn't syncing on one or more chains currently. Also take a look at the "suggestedFix" in the metadata of this command`,
source: name,
chainId: chain,
metadata: {
asyncTaskName: "blockLoader: fetching block data - `getBlock` rpc call",
suggestedFix: "This likely means the RPC url you are using is not responding correctly. Please try another RPC endipoint."
}
}), undefined, undefined, undefined, undefined);
let makeReceiptLoader = () => LazyLoader.make(transactionHash => {
Prometheus.SourceRequestCount.increment(name, chain, "eth_getTransactionReceipt");
return Rest.fetch(Rpc.GetTransactionReceipt.rawRoute, transactionHash, client);
}, (am, exn) => Logging.error({
err: Utils.prettifyExn(exn),
msg: `Top level promise timeout reached. Please review other errors or warnings in the code. This function will retry in ` + (am._retryDelayMillis / 1000 | 0).toString() + ` seconds. It is highly likely that your indexer isn't syncing on one or more chains currently. Also take a look at the "suggestedFix" in the metadata of this command`,
source: name,
chainId: chain,
metadata: {
asyncTaskName: "receiptLoader: fetching transaction receipt - `getTransactionReceipt` rpc call",
suggestedFix: "This likely means the RPC url you are using is not responding correctly. Please try another RPC endipoint."
}
}), undefined, undefined, undefined, undefined);
let blockLoader = {
contents: makeBlockLoader()
};
let transactionLoader = {
contents: makeTransactionLoader()
};
let receiptLoader = {
contents: makeReceiptLoader()
};
let getEventBlockOrThrow = makeThrowingGetEventBlock(blockNumber => LazyLoader.get(blockLoader.contents, blockNumber), lowercaseAddresses);
let getEventTransactionOrThrow = makeThrowingGetEventTransaction(async transactionHash => {
let json = await LazyLoader.get(transactionLoader.contents, transactionHash);
if (json !== undefined) {
return json;
}
throw {
RE_EXN_ID: TransactionDataNotFound,
message: `Transaction not found for hash: ` + transactionHash,
Error: new Error()
};
}, async transactionHash => {
let json = await LazyLoader.get(receiptLoader.contents, transactionHash);
if (json !== undefined) {
return json;
}
throw {
RE_EXN_ID: TransactionDataNotFound,
message: `Transaction receipt not found for hash: ` + transactionHash,
Error: new Error()
};
}, lowercaseAddresses);
let convertLogToHyperSyncEvent = log => {
let hyperSyncLog_removed = log.removed;
let hyperSyncLog_logIndex = log.logIndex;
let hyperSyncLog_transactionIndex = log.transactionIndex;
let hyperSyncLog_transactionHash = log.transactionHash;
let hyperSyncLog_blockHash = log.blockHash;
let hyperSyncLog_blockNumber = log.blockNumber;
let hyperSyncLog_address = Primitive_option.some(log.address);
let hyperSyncLog_data = log.data;
let hyperSyncLog_topics = log.topics;
let hyperSyncLog = {
removed: hyperSyncLog_removed,
logIndex: hyperSyncLog_logIndex,
transactionIndex: hyperSyncLog_transactionIndex,
transactionHash: hyperSyncLog_transactionHash,
blockHash: hyperSyncLog_blockHash,
blockNumber: hyperSyncLog_blockNumber,
address: hyperSyncLog_address,
data: hyperSyncLog_data,
topics: hyperSyncLog_topics
};
return {
log: hyperSyncLog
};
};
let hscDecoder = {
contents: undefined
};
let getHscDecoder = () => {
let decoder = hscDecoder.contents;
if (decoder !== undefined) {
return decoder;
}
let decoder$1 = HyperSyncClient.Decoder.fromParams(allEventParams, !lowercaseAddresses);
hscDecoder.contents = decoder$1;
return decoder$1;
};
let getItemsOrThrow = async (fromBlock, toBlock, addressesByContractName, indexingAddresses, knownHeight, partitionId, selection, retry, param) => {
let startFetchingBatchTimeRef = Hrtime.makeTimer();
let maxSuggestedBlockInterval = mutSuggestedBlockIntervals[maxSuggestedBlockIntervalKey];
let suggestedBlockInterval = maxSuggestedBlockInterval !== undefined ? maxSuggestedBlockInterval : Stdlib_Option.getOr(mutSuggestedBlockIntervals[partitionId], syncConfig.initialBlockInterval);
let toBlock$1 = toBlock !== undefined ? Primitive_int.min(toBlock, knownHeight) : knownHeight;
let suggestedToBlock = Primitive_int.max(Primitive_int.min((fromBlock + suggestedBlockInterval | 0) - 1 | 0, toBlock$1), fromBlock);
let firstBlockParentPromise = fromBlock > 0 ? LazyLoader.get(blockLoader.contents, fromBlock - 1 | 0).then(json => parseBlockInfo(json)) : Promise.resolve(undefined);
let match = getSelectionConfig(selection);
let match$1 = match.getLogSelectionOrThrow(addressesByContractName);
let match$2 = await getNextPage(fromBlock, suggestedToBlock, match$1.addresses, match$1.topicQuery, blockNumber => LazyLoader.get(blockLoader.contents, blockNumber).then(parseBlockInfo), syncConfig, client, mutSuggestedBlockIntervals, partitionId, name, chain);
let latestFetchedBlockInfo = match$2.latestFetchedBlockInfo;
let logs = match$2.logs;
let executedBlockInterval = (suggestedToBlock - fromBlock | 0) + 1 | 0;
if (executedBlockInterval >= suggestedBlockInterval && !(maxSuggestedBlockIntervalKey in mutSuggestedBlockIntervals)) {
mutSuggestedBlockIntervals[partitionId] = Primitive_int.min(executedBlockInterval + syncConfig.accelerationAdditive | 0, syncConfig.intervalCeiling);
}
let hyperSyncEvents = logs.map(convertLogToHyperSyncEvent);
let parsedEvents;
try {
parsedEvents = await getHscDecoder().decodeLogs(hyperSyncEvents);
} catch (raw_exn) {
let exn = Primitive_exceptions.internalToException(raw_exn);
throw {
RE_EXN_ID: Source.GetItemsError,
_1: {
TAG: "FailedGettingItems",
exn: exn,
attemptedToBlock: toBlock$1,
retry: {
TAG: "ImpossibleForTheQuery",
message: "Failed to parse events using hypersync client decoder. Please double-check your ABI."
}
},
Error: new Error()
};
}
let parsedQueueItems = await Promise.all(Stdlib_Array.filterMap(Stdlib_Array.zip(logs, parsedEvents), param => {
let log = param[0];
let topic0 = Stdlib_Option.getOr(log.topics[0], "0x0");
let routedAddress = lowercaseAddresses ? Address.Evm.fromAddressLowercaseOrThrow(log.address) : Address.Evm.fromAddressOrThrow(log.address);
let eventConfig = EventRouter.get(eventRouter, EventRouter.getEvmEventId(topic0, log.topics.length), routedAddress, log.blockNumber, indexingAddresses);
if (eventConfig === undefined) {
return;
}
let decoded = Stdlib_Option.flatMap(Primitive_option.fromNullable(param[1]), __x => __x[eventConfig.contractName]);
if (decoded === undefined) {
return;
}
let decoded$1 = Primitive_option.valFromOption(decoded);
return (async () => {
let match;
try {
match = await Promise.all([
getEventBlockOrThrow(log, eventConfig.selectedBlockFields),
getEventTransactionOrThrow(log, eventConfig.selectedTransactionFields)
]);
} catch (raw_exn) {
let exn = Primitive_exceptions.internalToException(raw_exn);
if (exn.RE_EXN_ID === TransactionDataNotFound) {
let backoffMillis = retry !== 0 ? 500 * retry | 0 : 100;
throw {
RE_EXN_ID: Source.GetItemsError,
_1: {
TAG: "FailedGettingItems",
exn: null,
attemptedToBlock: toBlock$1,
retry: {
TAG: "WithBackoff",
message: exn.message + `. The RPC provider might be load-balanced between nodes that drift independently slightly from the head. Indexing should continue correctly after retrying the query in ` + backoffMillis.toString() + `ms.`,
backoffMillis: backoffMillis
}
},
Error: new Error()
};
}
throw {
RE_EXN_ID: Source.GetItemsError,
_1: {
TAG: "FailedGettingFieldSelection",
exn: exn,
blockNumber: log.blockNumber,
logIndex: log.logIndex,
message: "Failed getting selected fields. Please double-check your RPC provider returns correct data."
},
Error: new Error()
};
}
let block = match[0];
return {
kind: 0,
eventConfig: eventConfig,
timestamp: block.timestamp,
chain: chain,
blockNumber: block.number,
blockHash: block.hash,
logIndex: log.logIndex,
event: {
contractName: eventConfig.contractName,
eventName: eventConfig.name,
params: decoded$1,
chainId: chain,
srcAddress: routedAddress,
logIndex: log.logIndex,
transaction: match[1],
block: block
}
};
})();
}));
let optFirstBlockParent = await firstBlockParentPromise;
let totalTimeElapsed = Hrtime.toSecondsFloat(Hrtime.timeSince(startFetchingBatchTimeRef));
let blockHashes = [];
let pushBlockInfo = b => {
blockHashes.push({
blockHash: b.hash,
blockNumber: b.number
});
if (b.number > 0) {
blockHashes.push({
blockHash: b.parentHash,
blockNumber: b.number - 1 | 0
});
return;
}
};
pushBlockInfo(latestFetchedBlockInfo);
if (optFirstBlockParent !== undefined) {
pushBlockInfo(optFirstBlockParent);
}
logs.forEach(log => {
blockHashes.push({
blockHash: log.blockHash,
blockNumber: log.blockNumber
});
});
return {
knownHeight: knownHeight,
blockHashes: blockHashes,
parsedQueueItems: parsedQueueItems,
fromBlockQueried: fromBlock,
latestFetchedBlockNumber: latestFetchedBlockInfo.number,
latestFetchedBlockTimestamp: latestFetchedBlockInfo.timestamp,
stats: {
"total time elapsed (s)": totalTimeElapsed
}
};
};
let onReorg = param => {
blockLoader.contents = makeBlockLoader();
transactionLoader.contents = makeTransactionLoader();
receiptLoader.contents = makeReceiptLoader();
};
let getBlockHashes = (blockNumbers, _currentlyUnusedLogger) => Stdlib_Promise.$$catch(Promise.all(blockNumbers.map(blockNum => LazyLoader.get(blockLoader.contents, blockNum))).then(rawBlocks => ({
TAG: "Ok",
_0: rawBlocks.map(json => {
let b = parseBlockInfo(json);
return {
blockHash: b.hash,
blockNumber: b.number,
blockTimestamp: b.timestamp
};
})
})), exn => Promise.resolve({
TAG: "Error",
_0: exn
}));
let createHeightSubscription = Stdlib_Option.map(param.ws, wsUrl => (onHeight => RpcWebSocketHeightStream.subscribe(wsUrl, chain, onHeight)));
return {
name: name,
sourceFor: param.sourceFor,
chain: chain,
poweredByHyperSync: false,
pollingInterval: syncConfig.pollingInterval,
getBlockHashes: getBlockHashes,
getHeightOrThrow: async () => {
let timerRef = Hrtime.makeTimer();
let height;
try {
height = await rpcClient.getHeight();
} catch (exn) {
let seconds = Hrtime.toSecondsFloat(Hrtime.timeSince(timerRef));
Prometheus.SourceRequestCount.increment(name, chain, "eth_blockNumber");
Prometheus.SourceRequestCount.addSeconds(name, chain, "eth_blockNumber", seconds);
throw exn;
}
let seconds$1 = Hrtime.toSecondsFloat(Hrtime.timeSince(timerRef));
Prometheus.SourceRequestCount.increment(name, chain, "eth_blockNumber");
Prometheus.SourceRequestCount.addSeconds(name, chain, "eth_blockNumber", seconds$1);
return height;
},
getItemsOrThrow: getItemsOrThrow,
createHeightSubscription: createHeightSubscription,
onReorg: onReorg
};
}
export {
QueryTimout,
TransactionDataNotFound,
getKnownRawBlock,
parseBlockInfo,
getKnownRawBlockWithBackoff,
getSuggestedBlockIntervalFromExn,
maxSuggestedBlockIntervalKey,
getNextPage,
getSelectionConfig,
memoGetSelectionConfig,
lowercaseAddressSchema,
checksumAddressSchema,
makeBlockFieldRegistry,
blockFieldRegistryLowercase,
blockFieldRegistryChecksum,
parseBlockFieldsFromJson,
makeThrowingGetEventBlock,
makeFieldRegistry,
fieldRegistryLowercase,
fieldRegistryChecksum,
parseFieldsFromJson,
makeThrowingGetEventTransaction,
make,
}
/* lowercaseAddressSchema Not a pure module */