Summary
Every read in app/lib/indexer-db.ts queries the shared indexer Postgres without a network predicate, while the Supabase paths in the same routes filter on network. The two backends therefore disagree about network isolation: whichever one a deployment happens to use decides whether a devnet trade can appear in mainnet figures.
Raised out of review on #2512 (CodeRabbit flagged the missing predicate on the new queryTraderStatsAggregate). It is not specific to that function — I checked the rest of the module and none of them filter either, so fixing one in isolation would make trader stats network-isolated while candles, funding and trade history on the same database stayed cross-network.
Affected
app/lib/indexer-db.ts — no occurrence of network in any WHERE clause. The column exists; the module's own schema comment documents it:
* trades — id, slab_address, trader, tx_signature, side, size,
* price, fee, created_at (timestamptz), network, ...
* funding_history — ... timestamp (timestamptz), network
Contrast the Supabase fallback in app/app/api/trader/[wallet]/stats/route.ts, which does:
.eq("network", getServerNetwork())
Impact
Bounded by deployment: it only matters where one Postgres instance holds rows for more than one network. If INDEXER_DATABASE_URL always points at a single-network database, this is latent rather than live — which is worth establishing before changing anything, because it decides whether this is a bug or a hardening.
Where it does bite, the symptom is quiet: devnet activity inflating mainnet trade counts, volume and "unique markets", with no error and no visible marker.
Prerequisite before fixing — please check this first
The two sides derive the network from different environment variables:
| side |
source |
values |
| indexer writes |
getNetwork() (@percolatorct/shared, dist/db/queries.js) → process.env.NETWORK |
"mainnet" | "devnet" |
| launch reads |
getServerNetwork() (app/lib/supabase.ts) → process.env.NEXT_PUBLIC_DEFAULT_NETWORK |
"mainnet" | "devnet" |
The vocabularies match, so a filter would compare like with like. But both default to devnet when their variable is unset, so a deployment running NETWORK=mainnet on the indexer with NEXT_PUBLIC_DEFAULT_NETWORK unset on launch would go from cross-network reads to zero rows for every wallet the moment a predicate is added.
Silently zeroing an endpoint is a worse failure than the one being fixed, so confirming both variables are set consistently in each environment should come before the code change.
Suggested fix
Once the env question is settled: thread getServerNetwork() through indexer-db.ts and add AND network = ${network} to each query, so both backends agree. Worth doing in one pass rather than per-function, so the module cannot end up half-isolated.
Not doing this in #2512
That PR is scoped to the 10k truncation in /api/trader/:wallet/stats, and its aggregate faithfully preserves the pre-existing (unfiltered) behaviour of the query it replaces. Fixing network isolation there alone would leave the module inconsistent and would carry the deployment risk above without anyone having checked it.
Summary
Every read in
app/lib/indexer-db.tsqueries the shared indexer Postgres without anetworkpredicate, while the Supabase paths in the same routes filter onnetwork. The two backends therefore disagree about network isolation: whichever one a deployment happens to use decides whether a devnet trade can appear in mainnet figures.Raised out of review on #2512 (CodeRabbit flagged the missing predicate on the new
queryTraderStatsAggregate). It is not specific to that function — I checked the rest of the module and none of them filter either, so fixing one in isolation would make trader stats network-isolated while candles, funding and trade history on the same database stayed cross-network.Affected
app/lib/indexer-db.ts— no occurrence ofnetworkin anyWHEREclause. The column exists; the module's own schema comment documents it:Contrast the Supabase fallback in
app/app/api/trader/[wallet]/stats/route.ts, which does:Impact
Bounded by deployment: it only matters where one Postgres instance holds rows for more than one network. If
INDEXER_DATABASE_URLalways points at a single-network database, this is latent rather than live — which is worth establishing before changing anything, because it decides whether this is a bug or a hardening.Where it does bite, the symptom is quiet: devnet activity inflating mainnet trade counts, volume and "unique markets", with no error and no visible marker.
Prerequisite before fixing — please check this first
The two sides derive the network from different environment variables:
getNetwork()(@percolatorct/shared,dist/db/queries.js) →process.env.NETWORK"mainnet"|"devnet"getServerNetwork()(app/lib/supabase.ts) →process.env.NEXT_PUBLIC_DEFAULT_NETWORK"mainnet"|"devnet"The vocabularies match, so a filter would compare like with like. But both default to
devnetwhen their variable is unset, so a deployment runningNETWORK=mainneton the indexer withNEXT_PUBLIC_DEFAULT_NETWORKunset on launch would go from cross-network reads to zero rows for every wallet the moment a predicate is added.Silently zeroing an endpoint is a worse failure than the one being fixed, so confirming both variables are set consistently in each environment should come before the code change.
Suggested fix
Once the env question is settled: thread
getServerNetwork()throughindexer-db.tsand addAND network = ${network}to each query, so both backends agree. Worth doing in one pass rather than per-function, so the module cannot end up half-isolated.Not doing this in #2512
That PR is scoped to the 10k truncation in
/api/trader/:wallet/stats, and its aggregate faithfully preserves the pre-existing (unfiltered) behaviour of the query it replaces. Fixing network isolation there alone would leave the module inconsistent and would carry the deployment risk above without anyone having checked it.