UNPKG

envio

Version:

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

1,260 lines (1,220 loc) • 43 kB
// 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 */