diff --git a/src/app/api/fee-compare/route.ts b/src/app/api/fee-compare/route.ts index b1c1c7e1..42b8c490 100644 --- a/src/app/api/fee-compare/route.ts +++ b/src/app/api/fee-compare/route.ts @@ -3,7 +3,7 @@ import { clientKey, rateLimit, tooManyRequests } from "@/lib/rate-limit"; import { keccak256 } from "js-sha3"; export const runtime = "nodejs"; -export const maxDuration = 30; +export const maxDuration = 50; const HL_API = "https://api.hyperliquid.xyz/info"; const GAINS_VARS_URL = "https://backend-arbitrum.gains.trade/trading-variables"; @@ -474,18 +474,26 @@ async function fetchGmxTrades(wallet: string, cutoffMs: number): Promise { const allFills: Array<{ fee: string; price: string; size: string; liquidity?: string; createdAt: string }> = []; - let page = 1; + // Use createdBeforeOrAt cursor pagination — newest first, no page= param + // page=N returns oldest-first and breaks the cutoff early-exit logic + let cursor: string | null = null; const limit = 100; - outer: while (page <= 20) { + outer: for (let i = 0; i < 20; i++) { + const params = new URLSearchParams({ + address: dydxAddress, + subaccountNumber: "0", + limit: String(limit), + }); + if (cursor) params.set("createdBeforeOrAt", cursor); + const res = await fetch( - `${DYDX_INDEXER}/v4/fills?address=${encodeURIComponent(dydxAddress)}&subaccountNumber=0&limit=${limit}&page=${page}`, + `${DYDX_INDEXER}/v4/fills?${params}`, { signal: AbortSignal.timeout(10000) } ); if (!res.ok) break; const body = (await res.json()) as { fills?: Array<{ fee: string; price: string; size: string; liquidity?: string; createdAt: string }>; - totalResults?: number; }; const pageFills = body.fills ?? []; if (pageFills.length === 0) break; @@ -496,26 +504,11 @@ async function fetchDydxFills(dydxAddress: string, cutoffMs: number): Promise; - }; - for (const p of pfBody.positions ?? []) { - fundingUsd += parseFloat(p.netFunding ?? "0"); - } - } - } catch { /* funding is optional */ } - + // Only count taker fills — maker fees are rebates and distort the comparison const takers = allFills.filter((f) => (f.liquidity ?? "").toUpperCase() !== "MAKER"); let feesUsdc = 0; let notionalUsd = 0; @@ -523,13 +516,12 @@ async function fetchDydxFills(dydxAddress: string, cutoffMs: number): Promise 0 ? (feesUsdc / notionalUsd) * 10000 : 0, }; @@ -540,7 +532,7 @@ async function fetchGainsTrades(wallet: string, cutoffMs: number): Promise - dYdX v4: public indexer with full pagination and date filter. Funding from{" "} - perpetualPositions.netFunding. Address must be{" "} + dYdX v4: public indexer with full pagination and date filter. Taker fills only. Funding excluded (not date-scopable from public API). Address must be{" "} dydx1... Cosmos format.{" "} )}