UNPKG

@uniswap/smart-order-router

Version:
288 lines 25 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SubgraphProvider = exports.BASE_V4_PAGE_SIZE = exports.PAGE_SIZE = void 0; const router_sdk_1 = require("@uniswap/router-sdk"); const sdk_core_1 = require("@uniswap/sdk-core"); const async_retry_1 = __importDefault(require("async-retry")); const await_timeout_1 = __importDefault(require("await-timeout")); const graphql_request_1 = require("graphql-request"); const lodash_1 = __importDefault(require("lodash")); const util_1 = require("../util"); exports.PAGE_SIZE = 1000; // 1k is max possible query size from subgraph. exports.BASE_V4_PAGE_SIZE = 3500; // TheGraph v4 base max pagesize is 3600. class SubgraphProvider { constructor(protocol, chainId, retries = 2, timeout = 30000, rollback = true, trackedEthThreshold = 0.01, trackedZoraEthThreshold = 0.001, zoraHooks, // @ts-expect-error - kept for backward compatibility untrackedUsdThreshold = Number.MAX_VALUE, subgraphUrl, bearerToken) { this.protocol = protocol; this.chainId = chainId; this.retries = retries; this.timeout = timeout; this.rollback = rollback; this.trackedEthThreshold = trackedEthThreshold; this.trackedZoraEthThreshold = trackedZoraEthThreshold; this.zoraHooks = zoraHooks; this.untrackedUsdThreshold = untrackedUsdThreshold; this.subgraphUrl = subgraphUrl; this.bearerToken = bearerToken; this.protocol = protocol; if (!this.subgraphUrl) { throw new Error(`No subgraph url for chain id: ${this.chainId}`); } util_1.log.info('bearerToken is', this.bearerToken); if (this.bearerToken) { this.client = new graphql_request_1.GraphQLClient(this.subgraphUrl, { headers: { authorization: `Bearer ${this.bearerToken}`, }, }); } else { this.client = new graphql_request_1.GraphQLClient(this.subgraphUrl); } if (protocol === router_sdk_1.Protocol.V4 && this.zoraHooks.size === 0) { throw new Error('Zora hooks param is mandatory for V4'); } } async getPools(_currencyIn, _currencyOut, providerConfig) { const beforeAll = Date.now(); let blockNumber = (providerConfig === null || providerConfig === void 0 ? void 0 : providerConfig.blockNumber) ? await providerConfig.blockNumber : undefined; const pageSizeToUse = this.protocol === router_sdk_1.Protocol.V4 && this.chainId == sdk_core_1.ChainId.BASE ? exports.BASE_V4_PAGE_SIZE : exports.PAGE_SIZE; util_1.log.info(`Getting ${this.protocol} pools from the subgraph with page size ${pageSizeToUse}${(providerConfig === null || providerConfig === void 0 ? void 0 : providerConfig.blockNumber) ? ` as of block ${providerConfig === null || providerConfig === void 0 ? void 0 : providerConfig.blockNumber}` : ''}.`); // Define separate queries for each filtering condition const queries = [ // 1. Pools with high tracked ETH (for both V3 and V4) { name: 'High tracked ETH pools', query: (0, graphql_request_1.gql) ` query getHighTrackedETHPools($pageSize: Int!, $id: String, $threshold: String!) { pools( first: $pageSize ${blockNumber ? `block: { number: ${blockNumber} }` : ``} where: { id_gt: $id, totalValueLockedETH_gt: $threshold } ) { ${this.getPoolFields()} } } `, variables: { threshold: this.trackedEthThreshold.toString() }, }, // 2. V4: Non-Zora pools with liquidity > 0 ...(this.protocol === router_sdk_1.Protocol.V4 ? [ { name: 'V4 non-Zora high liquidity pools', query: (0, graphql_request_1.gql) ` query getV4NonZoraHighLiquidityPools($pageSize: Int!, $id: String, $zoraHooks: [String!]!) { pools( first: $pageSize ${blockNumber ? `block: { number: ${blockNumber} }` : ``} where: { id_gt: $id, liquidity_gt: "0", hooks_not_in: $zoraHooks } ) { ${this.getPoolFields()} } } `, variables: { zoraHooks: Array.from(this.zoraHooks) }, }, // 3. V4: Zora pools with liquidity > 0 AND TVL > trackedZoraEthThreshold { name: 'V4 Zora high liquidity pools', query: (0, graphql_request_1.gql) ` query getV4ZoraHighLiquidityPools($pageSize: Int!, $id: String, $zoraHooks: [String!]!, $zoraThreshold: String!) { pools( first: $pageSize ${blockNumber ? `block: { number: ${blockNumber} }` : ``} where: { id_gt: $id, liquidity_gt: "0", hooks_in: $zoraHooks, totalValueLockedETH_gt: $zoraThreshold } ) { ${this.getPoolFields()} } } `, variables: { zoraHooks: Array.from(this.zoraHooks), zoraThreshold: this.trackedZoraEthThreshold.toString(), }, }, ] : []), // 4. V3: Pools with liquidity > 0 AND totalValueLockedETH = 0 (special V3 condition) ...(this.protocol === router_sdk_1.Protocol.V3 ? [ { name: 'V3 zero ETH pools', query: (0, graphql_request_1.gql) ` query getV3ZeroETHPools($pageSize: Int!, $id: String) { pools( first: $pageSize ${blockNumber ? `block: { number: ${blockNumber} }` : ``} where: { id_gt: $id, liquidity_gt: "0", totalValueLockedETH: "0" } ) { ${this.getPoolFields()} } } `, variables: {}, }, ] : []), ]; let allPools = []; let retries = 0; await (0, async_retry_1.default)(async () => { const timeout = new await_timeout_1.default(); const fetchPoolsForQuery = async (queryConfig) => { let lastId = ''; let pools = []; let poolsPage = []; let totalPages = 0; do { totalPages += 1; const start = Date.now(); util_1.log.info(`Starting fetching for ${queryConfig.name} page ${totalPages} with page size ${pageSizeToUse}`); const poolsResult = await this.client.request(queryConfig.query, Object.assign({ pageSize: pageSizeToUse, id: lastId }, queryConfig.variables)); poolsPage = poolsResult.pools; pools = pools.concat(poolsPage); if (pools.length > 0) { lastId = pools[pools.length - 1].id; } util_1.metric.putMetric(`${this.protocol}SubgraphProvider.chain_${this.chainId}.getPools.${queryConfig.name .replace(/\s+/g, '_') .toLowerCase()}.paginate.pageSize`, poolsPage.length); util_1.log.info(`Fetched ${poolsPage.length} pools for ${queryConfig.name} in ${Date.now() - start}ms`); } while (poolsPage.length > 0); util_1.metric.putMetric(`${this.protocol}SubgraphProvider.chain_${this.chainId}.getPools.${queryConfig.name .replace(/\s+/g, '_') .toLowerCase()}.paginate`, totalPages); util_1.metric.putMetric(`${this.protocol}SubgraphProvider.chain_${this.chainId}.getPools.${queryConfig.name .replace(/\s+/g, '_') .toLowerCase()}.pools.length`, pools.length); return pools; }; try { // Fetch pools for each query in parallel const poolPromises = queries.map((queryConfig) => fetchPoolsForQuery(queryConfig)); const allPoolsArrays = await Promise.all(poolPromises); // Merge all results and deduplicate by pool ID const poolMap = new Map(); allPoolsArrays.forEach((pools) => { pools.forEach((pool) => { poolMap.set(pool.id, pool); }); }); allPools = Array.from(poolMap.values()); const getPoolsPromise = Promise.resolve(allPools); const timerPromise = timeout.set(this.timeout).then(() => { throw new Error(`Timed out getting pools from subgraph: ${this.timeout}`); }); allPools = await Promise.race([getPoolsPromise, timerPromise]); return; } catch (err) { util_1.log.error({ err }, `Error fetching ${this.protocol} Subgraph Pools.`); throw err; } finally { timeout.clear(); } }, { retries: this.retries, onRetry: (err, retry) => { retries += 1; if (this.rollback && blockNumber && lodash_1.default.includes(err.message, 'indexed up to')) { util_1.metric.putMetric(`${this.protocol}SubgraphProvider.chain_${this.chainId}.getPools.indexError`, 1); blockNumber = blockNumber - 10; util_1.log.info(`Detected subgraph indexing error. Rolled back block number to: ${blockNumber}`); } util_1.metric.putMetric(`${this.protocol}SubgraphProvider.chain_${this.chainId}.getPools.timeout`, 1); allPools = []; util_1.log.info({ err }, `Failed to get pools from subgraph. Retry attempt: ${retry}`); }, }); util_1.metric.putMetric(`${this.protocol}SubgraphProvider.chain_${this.chainId}.getPools.retries`, retries); const beforeFilter = Date.now(); let poolsSanitized = []; if (this.protocol === router_sdk_1.Protocol.V3) { // Special treatment for all V3 pools in order to reduce latency due to thousands of pools with very low TVL locked // - Include "parseFloat(pool.totalValueLockedETH) === 0" as in certain occasions we have no way of calculating derivedETH so this is 0 poolsSanitized = allPools .filter((pool) => (parseInt(pool.liquidity) > 0 && parseFloat(pool.totalValueLockedETH) === 0) || parseFloat(pool.totalValueLockedETH) > this.trackedEthThreshold) .map((pool) => { return this.mapSubgraphPool(pool); }); } else if (this.protocol === router_sdk_1.Protocol.V4) { // For V4, apply additional filtering as a safety measure even though queries are optimized poolsSanitized = allPools .filter((pool) => { const liquidity = parseInt(pool.liquidity); const tvl = parseFloat(pool.totalValueLockedETH); const hooks = pool.hooks; const isZora = this.zoraHooks.has(hooks); if (isZora) { return tvl > this.trackedZoraEthThreshold; } return liquidity > 0 || tvl > this.trackedEthThreshold; }) .map((pool) => { return this.mapSubgraphPool(pool); }); } util_1.metric.putMetric(`${this.protocol}SubgraphProvider.chain_${this.chainId}.getPools.filter.latency`, Date.now() - beforeFilter); util_1.metric.putMetric(`${this.protocol}SubgraphProvider.chain_${this.chainId}.getPools.filter.length`, poolsSanitized.length); util_1.metric.putMetric(`${this.protocol}SubgraphProvider.chain_${this.chainId}.getPools.filter.percent`, (poolsSanitized.length / allPools.length) * 100); util_1.metric.putMetric(`${this.protocol}SubgraphProvider.chain_${this.chainId}.getPools`, 1); util_1.metric.putMetric(`${this.protocol}SubgraphProvider.chain_${this.chainId}.getPools.latency`, Date.now() - beforeAll); util_1.log.info(`Got ${allPools.length} ${this.protocol} pools from the subgraph (after deduplication). ${poolsSanitized.length} after filtering`); return poolsSanitized; } // Helper method to get the pool fields for GraphQL queries getPoolFields() { return ` id token0 { symbol id } token1 { symbol id } feeTier liquidity totalValueLockedUSD totalValueLockedETH `; } } exports.SubgraphProvider = SubgraphProvider; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3ViZ3JhcGgtcHJvdmlkZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvcHJvdmlkZXJzL3N1YmdyYXBoLXByb3ZpZGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLG9EQUErQztBQUMvQyxnREFBNkQ7QUFDN0QsOERBQWdDO0FBQ2hDLGtFQUFvQztBQUNwQyxxREFBcUQ7QUFDckQsb0RBQXVCO0FBR3ZCLGtDQUFzQztBQWF6QixRQUFBLFNBQVMsR0FBRyxJQUFJLENBQUMsQ0FBQywrQ0FBK0M7QUFDakUsUUFBQSxpQkFBaUIsR0FBRyxJQUFJLENBQUMsQ0FBQyx5Q0FBeUM7QUFpQ2hGLE1BQXNCLGdCQUFnQjtJQU1wQyxZQUNVLFFBQWtCLEVBQ2xCLE9BQWdCLEVBQ2hCLFVBQVUsQ0FBQyxFQUNYLFVBQVUsS0FBSyxFQUNmLFdBQVcsSUFBSSxFQUNmLHNCQUFzQixJQUFJLEVBQzFCLDBCQUEwQixLQUFLLEVBQy9CLFNBQXNCO0lBQzlCLHFEQUFxRDtJQUM3Qyx3QkFBd0IsTUFBTSxDQUFDLFNBQVMsRUFDeEMsV0FBb0IsRUFDcEIsV0FBb0I7UUFYcEIsYUFBUSxHQUFSLFFBQVEsQ0FBVTtRQUNsQixZQUFPLEdBQVAsT0FBTyxDQUFTO1FBQ2hCLFlBQU8sR0FBUCxPQUFPLENBQUk7UUFDWCxZQUFPLEdBQVAsT0FBTyxDQUFRO1FBQ2YsYUFBUSxHQUFSLFFBQVEsQ0FBTztRQUNmLHdCQUFtQixHQUFuQixtQkFBbUIsQ0FBTztRQUMxQiw0QkFBdUIsR0FBdkIsdUJBQXVCLENBQVE7UUFDL0IsY0FBUyxHQUFULFNBQVMsQ0FBYTtRQUV0QiwwQkFBcUIsR0FBckIscUJBQXFCLENBQW1CO1FBQ3hDLGdCQUFXLEdBQVgsV0FBVyxDQUFTO1FBQ3BCLGdCQUFXLEdBQVgsV0FBVyxDQUFTO1FBRTVCLElBQUksQ0FBQyxRQUFRLEdBQUcsUUFBUSxDQUFDO1FBQ3pCLElBQUksQ0FBQyxJQUFJLENBQUMsV0FBVyxF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