Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions src/app/api/fee-compare/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1110,27 +1110,36 @@
}

function reconstructGainsPositions(trades: GainsApiTrade[], cutoffMs: number): PositionSlice[] {
const OPEN_ACTIONS = new Set(["MarketOpened", "LimitOrderExecuted"]);
// v5 names: MarketOpened, LimitOrderExecuted — v6 names: TradeOpenedMarket, TradeOpenedLimit
const OPEN_ACTIONS = new Set(["MarketOpened", "LimitOrderExecuted", "TradeOpenedMarket", "TradeOpenedLimit"]);
// TradePosSizeIncrease updates the position size; use latest size as notional
const INCREASE_ACTIONS = new Set(["TradePosSizeIncrease"]);
const CLOSE_ACTIONS = new Set(["TradeClosedMarket", "TradeClosedTP", "TradeClosedSL", "TradeClosedLIQ"]);

const byId = new Map<number, { open?: GainsApiTrade; close?: GainsApiTrade }>();
for (const t of trades) {
if (!byId.has(t.id)) byId.set(t.id, {});
const e = byId.get(t.id)!;
if (OPEN_ACTIONS.has(t.action)) e.open = t;
else if (INCREASE_ACTIONS.has(t.action) && e.open) {
e.open = { ...e.open, size: t.size, leverage: t.leverage };
}
else if (CLOSE_ACTIONS.has(t.action)) e.close = t;
}

const now = Date.now();
const slices: PositionSlice[] = [];
for (const { open, close } of byId.values()) {
if (!open || !close) continue;
if (!open) continue;
const openMs = new Date(open.date).getTime();
if (openMs < cutoffMs) continue;
// Still-open positions use now as close time (same as reconstructHlPositions)
const closeMs = close ? new Date(close.date).getTime() : now;
slices.push({
coin: open.pair.split("/")[0],
notionalUsd: open.size * open.leverage,
openMs,
closeMs: new Date(close.date).getTime(),
closeMs,
isLong: open.buy !== false,
});
}
Expand Down Expand Up @@ -1204,7 +1213,7 @@
}

// Estimate GMX borrow fees for a set of position slices.
function estimateGmxBorrowFees(

Check warning on line 1216 in src/app/api/fee-compare/route.ts

View workflow job for this annotation

GitHub Actions / check

'estimateGmxBorrowFees' is defined but never used

Check warning on line 1216 in src/app/api/fee-compare/route.ts

View workflow job for this annotation

GitHub Actions / check

'estimateGmxBorrowFees' is defined but never used
positions: PositionSlice[],
borrowPerSecPerCoin: Record<string, number>
): number {
Expand Down
35 changes: 33 additions & 2 deletions src/lib/aggregate-blob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,37 @@ import type { Benchmark } from "@/types/benchmark";
import { loadSpecsUncached } from "@/lib/materialize/load";
import { overlayEditorial, slimBenchmarkForCache } from "@/lib/spec";

// Aggressive slim for the aggregate blob. Hub pages (homepage, categories,
// chains, products) only need card data — they never render editorial text
// or metric panels. Stripping these fields drops the serialized aggregate
// from ~4.3 MB to well under the 2 MB unstable_cache ceiling.
//
// Fields stripped beyond slimBenchmarkForCache (which already removes
// 7d/30d series):
// - extras.seriesByRegion24h (only used on bench detail pages)
// - metricPanels (only used on bench detail pages)
// - seoIntro, faq, disclaimer (editorial, bench detail only)
// - perChainExplainer (bench detail + worker's sitemap.json handles sitemap)
// - findings, methodology (bench detail only; required fields → [])
// - abstract (bench detail only; required field → "")
function slimForBlobAggregate(b: Benchmark): Benchmark {
const base = slimBenchmarkForCache(b);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { seriesByRegion24h: _sbr, ...slimExtras } = base.extras;
return {
...base,
extras: slimExtras,
metricPanels: undefined,
seoIntro: undefined,
faq: undefined,
perChainExplainer: undefined,
disclaimer: undefined,
findings: [],
methodology: [],
abstract: "",
};
}

// On any Vercel deployment (production or preview), use the self-hosted
// CDN proxy (/api/aggregate on openchainbench.com) so Vercel functions
// pay ~1 ms (edge cache hit) instead of ~12 s fetching the 7.5 MB blob
Expand Down Expand Up @@ -113,7 +144,7 @@ async function fetchAndProject(): Promise<Benchmark[] | null> {
for (const bench of raw.benches) {
const spec = specBySlug.get(bench.slug);
if (!spec) continue; // Bench in blob no longer has a spec — skip.
projected.push(slimBenchmarkForCache(overlayEditorial(bench, spec)));
projected.push(slimForBlobAggregate(overlayEditorial(bench, spec)));
}
return projected.sort((a, b) =>
(a.number ?? "").localeCompare(b.number ?? ""),
Expand All @@ -130,6 +161,6 @@ async function fetchAndProject(): Promise<Benchmark[] | null> {
*/
export const loadAggregateFromBlob = unstable_cache(
fetchAndProject,
["aggregate-blob-v2"],
["aggregate-blob-v3"],
{ revalidate: 60, tags: ["bench-aggregate", "benchmarks"] },
);
Loading