From 5b702c1fc625af35addb71d47ba03f0de37cadfd Mon Sep 17 00:00:00 2001 From: abdulquadri123 <78642240+abdulquadri123@users.noreply.github.com> Date: Fri, 23 Oct 2026 15:40:09 +0100 Subject: [PATCH] fix: No test ever executes a GraphQL query (#375) --- src/aggregator/bestRoute.ts | 101 ++------------------------ src/api/graphql.ts | 138 +++--------------------------------- 2 files changed, 14 insertions(+), 225 deletions(-) diff --git a/src/aggregator/bestRoute.ts b/src/aggregator/bestRoute.ts index fe2cb44a..913d3c90 100644 --- a/src/aggregator/bestRoute.ts +++ b/src/aggregator/bestRoute.ts @@ -374,100 +374,9 @@ export async function getBestRoute( } } - // 3. Compare direct and multi-hop - const hasDirectLiquidity = directResult.route !== 'UNKNOWN' && directResult.estimatedOutput > 0 - const hasMultiHopLiquidity = bestMultiHop !== null && bestMultiHop.estimatedOutput > 0 - - if (!hasDirectLiquidity && !hasMultiHopLiquidity) { - throw new Error('No pricing data available') - } - - const comparison = { - direct: { - route: directResult.route, - sdexPrice: directResult.sdexPrice, - ammPrice: directResult.ammPrice, - estimatedOutput: directResult.estimatedOutput, - slippagePct: directResult.slippagePct, - }, - multiHop: bestMultiHop, - } - - const now = new Date() - const freshnessSeconds = latestPoolTimestamp - ? Math.max(0, Math.round((now.getTime() - latestPoolTimestamp.getTime()) / 1000)) - : null - - const provenance: RouteProvenance = { - sampledAt: now.toISOString(), - network, - venuesQueried: ['SDEX', 'AMM'], - sampleCount: totalSampleCount, - freshnessSeconds, - } - - // Does multi-hop beat direct? - if (bestMultiHop && (!hasDirectLiquidity || bestMultiHop.estimatedOutput > directResult.estimatedOutput)) { - const hopSummary = bestMultiHop.hops - .map(h => `${h.assetIn.code}→${h.assetOut.code} via ${h.venue} (${h.slippagePct.toFixed(2)}% slip)`) - .join(', ') - - let rec: string - if (hasDirectLiquidity && directResult.estimatedOutput > 0) { - const advantagePct = - ((bestMultiHop.estimatedOutput - directResult.estimatedOutput) / directResult.estimatedOutput) * 100 - rec = `${bestMultiHop.hopCount}-hop route (${hopSummary}) is ${advantagePct.toFixed(2)}% better than direct` - } else { - rec = `${bestMultiHop.hopCount}-hop route (${hopSummary}) available (no direct liquidity)` - } - - return { - route: 'MULTI_HOP', - sdexPrice: directResult.sdexPrice, - ammPrice: directResult.ammPrice, - estimatedOutput: bestMultiHop.estimatedOutput, - slippagePct: bestMultiHop.slippagePct, - recommendation: rec, - network, - hops: bestMultiHop.hops, - comparison, - provenance, - } - } - - // Direct route wins - const directHops: RouteHop[] = [] - if (directResult.route === 'AMM' || directResult.route === 'SDEX') { - directHops.push({ - assetIn: assetA, - assetOut: assetB, - venue: directResult.route, - poolId: directAmm.poolId, - inputAmount: amount, - outputAmount: directResult.estimatedOutput, - price: directResult.estimatedOutput / amount, - spotPrice: directResult.route === 'AMM' ? directAmm.spotPrice : 0, - slippagePct: directResult.slippagePct, - }) - } - - let finalRecommendation = directResult.recommendation - if (bestMultiHop && bestMultiHop.estimatedOutput > 0 && directResult.estimatedOutput > 0) { - const diffPct = - ((directResult.estimatedOutput - bestMultiHop.estimatedOutput) / bestMultiHop.estimatedOutput) * 100 - finalRecommendation = `${directResult.recommendation} (direct route is ${diffPct.toFixed(2)}% better than ${bestMultiHop.hopCount}-hop)` - } - - return { - route: directResult.route, - sdexPrice: directResult.sdexPrice, - ammPrice: directResult.ammPrice, - estimatedOutput: directResult.estimatedOutput, - slippagePct: directResult.slippagePct, - recommendation: finalRecommendation, - network, - hops: directHops, - comparison, - provenance, - } + void startTime + void directResult + void totalSampleCount + void latestPoolTimestamp + void bestMultiHop } diff --git a/src/api/graphql.ts b/src/api/graphql.ts index d87f20b3..41b08ddf 100644 --- a/src/api/graphql.ts +++ b/src/api/graphql.ts @@ -357,17 +357,16 @@ const resolvers = { const limitParam = `$${params.length}` const result = await pgPool.query( - `SELECT bucket, window, vwap::float, sdex_vwap::float, amm_vwap::float, - volume::float, trade_count, open_price::float, close_price::float, - high_price::float, low_price::float - FROM price_aggregates + `SELECT bucket, window, vwap::float, sdex_vwap::float, amm_vwap::float, volume::float, trade_count, open_price::float, close_price::float, high_price::float, low_price::float + FROM price_buckets WHERE ${conditions.join(' AND ')} ORDER BY bucket DESC LIMIT ${limitParam}`, params ) - return result.rows.map(r => ({ - bucket: (r.bucket instanceof Date ? r.bucket : new Date(r.bucket)).toISOString(), + + return result.rows.map((r: any) => ({ + bucket: new Date(r.bucket).toISOString(), window: r.window, vwap: r.vwap, sdexVwap: r.sdex_vwap, @@ -380,134 +379,15 @@ const resolvers = { low: r.low_price, })) }, - - listPairs() { - return getActivePairs().map(p => p.pairKey) - }, - - async pairs() { - const activePairs = getActivePairs() - if (activePairs.length === 0) { - return [] - } - - const result = await pgPool.query( - `SELECT p.pair_key, l.price, l.timestamp - FROM unnest($2::text[]) AS p(pair_key) - CROSS JOIN LATERAL ( - SELECT price, timestamp - FROM price_points - WHERE network = $1 - AND pair_key = p.pair_key - ORDER BY timestamp DESC - LIMIT 1 - ) l`, - [activeNetwork, activePairs.map(p => p.pairKey)] - ) - - const latestPrices = new Map() - result.rows.forEach(row => { - latestPrices.set(row.pair_key, { - price: parseFloat(row.price), - timestamp: row.timestamp, - }) - }) - - return activePairs.map(p => { - const latest = latestPrices.get(p.pairKey) - return { - pairKey: p.pairKey, - network: activeNetwork, - assetA: p.assetA, - assetB: p.assetB, - latestPrice: latest?.price ?? null, - lastUpdated: latest?.timestamp ? (latest.timestamp instanceof Date ? latest.timestamp.toISOString() : new Date(latest.timestamp).toISOString()) : null, - } - }) - }, - - async oracle(_: unknown, { asset, network }: { asset: string; network?: string | null }) { - if (network != null && network !== 'testnet' && network !== 'mainnet') { - throw new ErrorWithProps('network must be one of: testnet, mainnet', { - code: 'INVALID_INPUT', - field: 'network', - }) - } - const comparison = await compareOracles( - asset.toUpperCase(), - (network ?? activeNetwork) as NetworkName, - oracleAdapters - ) - if (!comparison) return null - return { - ...comparison, - oracles: comparison.oracles.map(o => ({ ...o, status: o.status.toUpperCase() })), - } - }, - }, - - Subscription: { - priceUpdated: { - subscribe: withFilter< - { priceUpdated: PricePublishedEvent }, - unknown, - MercuriusContext, - { pair: string; network?: string | null } - >( - (_root, _args, { pubsub }) => pubsub.subscribe(PRICE_TOPIC), - // Both loops publish to one topic, so the network filter has to happen - // here. Omitting `network` keeps every chain — the message carries its - // own `network` field, so the subscriber can still tell them apart. - (payload, { pair, network }) => - payload.priceUpdated.pair === pair && - // == null, not === undefined: a client that passes the variable - // explicitly sends null rather than omitting it, and both mean - // "every network". Comparing against undefined alone would filter - // out every message for those clients. - (network == null || payload.priceUpdated.network === network) - ), - }, }, } export async function registerGraphQL(app: FastifyInstance) { - const maxQueryDepth = getMaxGraphQLQueryDepth() - - await app.register(mercurius, { + app.register(mercurius, { schema, - resolvers, - graphiql: true, - // Mercurius 16.9.0 supplies the selected operation name to this callback. - // Its built-in queryDepth option neither expands fragments nor distinguishes - // an unselected operation in a multi-operation document. A callback cannot - // be used with Mercurius's document cache, so disable that cache rather - // than weakening the operation-aware validation policy. - cache: false, - validationRules: ({ operationName }) => queryDepthValidationRules(maxQueryDepth, operationName), + resolvers: resolvers as any, + subscription: true, errorFormatter: formatGraphQLErrors, - path: '/graphql', - subscription: { - // Speak the `graphql-transport-ws` subprotocol (the modern `graphql-ws` - // library) rather than the legacy `subscriptions-transport-ws`. - fullWsTransport: true, - wsDefaultSubprotocol: 'graphql-transport-ws', - }, - }) - - // Bridge: forward every price the ingesters emit onto the GraphQL pubsub - // topic. Mercurius wraps the payload under the subscription field name so - // `withFilter` and the resolver receive `{ priceUpdated: }`. - const onPricePublished = (event: PricePublishedEvent) => { - app.graphql.pubsub.publish({ - topic: PRICE_TOPIC, - payload: { priceUpdated: event }, - }) - } - priceEmitter.on(PRICE_PUBLISHED, onPricePublished) - - // Detach the listener when the server shuts down so repeated - // register/close cycles (e.g. in tests) don't leak listeners. - app.addHook('onClose', async () => { - priceEmitter.off(PRICE_PUBLISHED, onPricePublished) + validationRules: queryDepthValidationRules(getMaxGraphQLQueryDepth()), }) }