From fd412156108bbfaeaefcd1648c5fe6833177162a Mon Sep 17 00:00:00 2001
From: Florent Tapponnier <160007691+Flotapponnier@users.noreply.github.com>
Date: Tue, 8 Sep 2026 16:27:47 +0200
Subject: [PATCH] fix(products): per-chain chips show real leadership only,
with the denominator
Two problems on /products/[slug], both visible on Serialized.
rankPerChainForBench only holds one real per-chain fact: the leader, from
bestPerChain. For every other provider it reused the unfiltered aggregate
order shifted by one slot, so a chip reading "#3 on Solana" was the global
rank with a chain label on it, repeated identically across every chain of
the bench. That reads as a measurement and is not one. Non-leader chips
are gone: a chip now means "leads this chain", and its absence means
"does not lead", not "ranks lower".
The chips also hid how many providers were measured on the chain. "#1 on
Solana" was #1 of 2 on bench 008 while sitting next to "#3 of 8" for the
aggregate. They now read "#1 of 4 on Ethereum", with the denominator taken
from providersPerChain and omitted when that set is unknown rather than
substituting the global count.
Wins accounting is unchanged: it already counted rank === 1 entries only.
Co-Authored-By: Claude Opus 5 (1M context)
---
src/app/products/[slug]/page.tsx | 21 ++++---
src/components/bench-appearances-section.tsx | 9 +--
src/lib/providers.ts | 60 +++++++++-----------
3 files changed, 41 insertions(+), 49 deletions(-)
diff --git a/src/app/products/[slug]/page.tsx b/src/app/products/[slug]/page.tsx
index 1404fe50d..1992396b1 100644
--- a/src/app/products/[slug]/page.tsx
+++ b/src/app/products/[slug]/page.tsx
@@ -782,11 +782,13 @@ export default async function ProviderPage({
const catColor = CATEGORY_COLOR[a.benchmark.category];
const hasData = a.rank > 0 && a.result.ms.p50 !== 0;
const value = hasData ? fmtUnit(a.result.ms.p50, a.benchmark.unit) : null;
- // Per-chain rank chips. Rendered alongside the aggregate rank
- // when the bench declares chain dimensions and the provider has
- // per-chain ranks populated. Surface text reads e.g. "#1 on
- // Solana · #4 on Base · #4 on BNB" so a chain-restricted
- // provider can't be passed off as a free cross-chain #1.
+ // Per-chain leadership chips, rendered alongside the aggregate
+ // rank when the bench declares chain dimensions. Leaders only:
+ // a chip means "leads this chain", and its absence means "does
+ // not lead", never "ranks lower". Reads e.g. "#1 of 4 on
+ // Ethereum" so a chain-restricted provider can't be passed off
+ // as a free cross-chain #1, and so a win on a two-provider
+ // chain isn't dressed up as a win on a crowded one.
const chainRanks =
a.rankPerChain && a.benchmark.chainDimensions
? a.benchmark.chainDimensions
@@ -840,13 +842,10 @@ export default async function ProviderPage({
{chainRanks.map(({ chain, entry }) => (
- #{entry.rank} on {chain.label}
+ #1{entry.totalRanked > 0 ? ` of ${entry.totalRanked}` : ""} on{" "}
+ {chain.label}
))}
diff --git a/src/components/bench-appearances-section.tsx b/src/components/bench-appearances-section.tsx
index c50c7fc38..de5582033 100644
--- a/src/components/bench-appearances-section.tsx
+++ b/src/components/bench-appearances-section.tsx
@@ -82,13 +82,10 @@ export async function BenchAppearancesSection({ providerSlug }: Props) {
{chainRanks.map(({ chain, entry }) => (
- #{entry.rank} on {chain.label}
+ #1{entry.totalRanked > 0 ? ` of ${entry.totalRanked}` : ""} on{" "}
+ {chain.label}
))}
diff --git a/src/lib/providers.ts b/src/lib/providers.ts
index 8588a77d4..0301b7333 100644
--- a/src/lib/providers.ts
+++ b/src/lib/providers.ts
@@ -200,11 +200,12 @@ export type ProviderAppearance = {
result: ProviderResult;
rank: number;
totalRanked: number;
- /** Per-chain rank for this provider on this bench. Only populated when
- * the bench declares chain dimensions AND bestPerChain has at least one
- * entry. Key = chain slug (matching dimensions.chain[].value), value =
- * { rank, totalRanked } computed within the providers present on that
- * chain. Renderers can fall back to `rank` when this is empty. */
+ /** Per-chain leadership for this provider on this bench. Populated only
+ * for chains this provider *leads*, so `rank` is always 1 and an absent
+ * key means "does not lead here", never "ranked lower here". Key = chain
+ * slug (matching dimensions.chain[].value). `totalRanked` is how many
+ * providers were measured on that chain, or 0 when that set is unknown.
+ * Renderers fall back to the aggregate `rank` when this is empty. */
rankPerChain?: Record;
};
@@ -245,18 +246,23 @@ function rankProviders(b: Benchmark): ProviderResult[] {
}
/**
- * Compute per-chain rank for every provider on a bench. Bench must declare
- * `dimensions.chain` and have a non-empty `bestPerChain` for any rank to be
+ * Compute per-chain leadership for a bench. Bench must declare
+ * `dimensions.chain` and have a non-empty `bestPerChain` for anything to be
* recorded.
*
- * Approximation: spec.ts only stashes the *leader* per chain (one extra Prom
- * roundtrip per chain). To express other providers' rank-per-chain, we use a
- * coarse fallback: anyone present in the unfiltered `results` is ranked by
- * the bench's standard direction (lower-is-better or higher-is-better)
- * within the live result set, and the leader's slot is forcibly overridden
- * with rank 1 for that chain. This is a soft signal — the bench page chain
- * tabs are authoritative — but it is enough to flag chain-restricted
- * providers like GMGN as "#1 on Solana only" on /products/[slug].
+ * Leaders only, deliberately. spec.ts stashes the *leader* per chain (one
+ * extra Prom roundtrip per chain), and that is the only per-chain fact we
+ * actually hold. An earlier version also emitted ranks for non-leaders by
+ * reusing the unfiltered aggregate order shifted by one slot; those chips
+ * rendered as "#3 on Solana" while being the provider's *global* rank with a
+ * chain label stuck on it, identical across every chain of the bench. That
+ * reads as a measurement and is not one, so it is gone: a provider now gets a
+ * chip for a chain only when it leads that chain.
+ *
+ * `totalRanked` is the number of providers actually measured on the chain,
+ * taken from `providersPerChain`. It is 0 when the bench has not stashed that
+ * set (older cached entries), and renderers must then omit the denominator
+ * rather than substitute the global count.
*/
function rankPerChainForBench(
b: Benchmark,
@@ -280,24 +286,14 @@ function rankPerChainForBench(
const presentSet = providersPerChain?.[chain.value]
? new Set(providersPerChain[chain.value].map((s) => s.toLowerCase()))
: undefined;
- const scoped = presentSet
- ? liveSorted.filter((r) => presentSet.has(r.slug.toLowerCase()))
- : liveSorted;
+ // Only claim a denominator when we know who was measured on this
+ // chain; scoped.length over the global list would be a different
+ // number wearing the same label.
+ const totalRanked = presentSet
+ ? liveSorted.filter((r) => presentSet.has(r.slug.toLowerCase())).length
+ : 0;
const perProvider = new Map();
- const leaderLc = leader.slug.toLowerCase();
- const leaderIdx = scoped.findIndex((r) => r.slug.toLowerCase() === leaderLc);
- scoped.forEach((r, idx) => {
- const lc = r.slug.toLowerCase();
- if (lc === leaderLc) {
- perProvider.set(lc, { rank: 1, totalRanked: scoped.length });
- return;
- }
- // Anyone ranked above the leader in the unfiltered set drops by one
- // slot here (since the leader skips ahead of them on this chain).
- const rankOnChain =
- leaderIdx !== -1 && idx < leaderIdx ? idx + 2 : idx + 1;
- perProvider.set(lc, { rank: rankOnChain, totalRanked: scoped.length });
- });
+ perProvider.set(leader.slug.toLowerCase(), { rank: 1, totalRanked });
out[chain.value] = perProvider;
}
return out;