Skip to content

fix(accounts): declare unpriceable positions instead of dropping them silently - #9306

Merged
JSONbored merged 1 commit into
mainfrom
fix/positions-unpriceable-degraded
Aug 3, 2026
Merged

fix(accounts): declare unpriceable positions instead of dropping them silently#9306
JSONbored merged 1 commit into
mainfrom
fix/positions-unpriceable-degraded

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

/api/v1/accounts/{ss58}/positions published position_count: 0,
total_stake_alpha: 0 and no degraded block for coldkeys that demonstrably hold positions in
the ledger it reads.

Coldkey 5Df7xwEP… is the first row of chain.nominator_positions — hotkey 5FyVinYp…,
netuid 18, share_fraction: 1 — and the route answered zero. Indistinguishable from "this account
delegates nothing."

The skip is correct; the silence was not

buildAccountPositions prices each position off the live neurons table and skips any it cannot
price. That skip stays: #9066 removed the unpriced_* escape hatches, so a position with no
known stake must not be published with a fabricated stake_tao: 0. What was missing is any signal
that it happened.

neurons carries only currently-registered neurons while the position ledger is a snapshot, so
the overlap is far worse than it looks — sampling eight distinct hotkeys from the live ledger, only
1 of 8 was present in neurons. And neurons itself is healthy (129 netuids, 30,085 rows,
captured minutes ago), so this does not heal on its own.

Fix

POSITIONS_DEGRADED_UNPRICEABLE, set whenever any row was dropped for want of a price.

Any, not all: a partially-priced total understates the account just as silently and is harder
to notice, because the payload looks healthy for carrying positions.

A malformed row is deliberately not counted — that is bad data, not an unpriced holding, and
conflating them would report a pricing problem that is not happening.

The existing snapshot_predates_stake_activity still wins when both apply: both mean "do not trust
this total", and that one says why more usefully and carries the two provenance stamps this pure
builder cannot see. When the annotator does not contradict the zero, this marker survives its pass
— both directions are tested.

No schema change. degraded.reason is a free-form string, and the block is absent on every
trustworthy answer, so a consumer that ignores it reads exactly what it read before.
validate:contract-drift passes and npm run build produces no diff.

Verification

Four mutations, each caught:

mutation tests failed
revert to the silent skip 4
mark only all-zero results 1
mark when nothing was dropped 3
count malformed rows as unpriceable 1

Patch coverage 9/9 = 100% with every branch taken, measured by intersecting the diff's line
ranges with v8's uncovered set. 457 tests pass across the positions suites plus
request-handlers-entities; tsc --noEmit, prettier and eslint clean.

Two things this does NOT fix, both filed in #9305

Refs #9305

… silently

/api/v1/accounts/{ss58}/positions published position_count 0,
total_stake_alpha 0 and NO degraded block for coldkeys that demonstrably hold
positions in the ledger it reads. Coldkey 5Df7xwEP... is the first row of
chain.nominator_positions (hotkey 5FyVinYp..., netuid 18, share_fraction 1) and
the route answered zero -- indistinguishable from "this account delegates
nothing".

buildAccountPositions prices each position off the live `neurons` table and
skips any it cannot price. That skip is CORRECT and stays: #9066 removed the
unpriced_* escape hatches, so a position with no known stake must not be
published with a fabricated 0 stake_tao. What was missing is any signal that it
happened.

`neurons` carries only currently-registered neurons while the position ledger
is a snapshot, so the overlap is much worse than it looks: sampling eight
distinct hotkeys from the live ledger, only ONE was present in `neurons`.
`neurons` itself is healthy -- 129 netuids, 30,085 rows, captured minutes ago
-- so this is not a stale-tier problem that will heal on its own.

Add POSITIONS_DEGRADED_UNPRICEABLE, set whenever ANY row was dropped for want
of a price. Any, not all: a partially-priced total understates the account just
as silently and is harder to notice, because the payload looks healthy for
carrying positions. A malformed row is deliberately NOT counted -- that is bad
data, not an unpriced holding, and conflating them would report a pricing
problem that is not happening.

The existing annotator's snapshot_predates_stake_activity still wins when both
apply. Both mean "do not trust this total"; that one says why more usefully and
carries the two provenance stamps this pure builder cannot see. When the
annotator does not contradict the zero, this marker survives its pass.

No schema change: degraded.reason is a free-form string, and the block is
absent on every trustworthy answer, so a consumer that ignores it reads exactly
what it read before.

Four mutations are each caught: reverting to the silent skip, marking only
all-zero results, marking when nothing was dropped, and counting malformed rows
as unpriceable.

Refs #9305
@superagent-security

Copy link
Copy Markdown

Superagent didn't find any vulnerabilities or security issues in this PR.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
metagraphed-data-api 3ace7f3 Aug 03 2026, 08:40 PM

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
metagraphed-registry-sync-api 3ace7f3 Aug 03 2026, 08:40 PM

@JSONbored JSONbored self-assigned this Aug 3, 2026
@JSONbored
JSONbored merged commit 9a2509c into main Aug 3, 2026
7 checks passed
@JSONbored
JSONbored deleted the fix/positions-unpriceable-degraded branch August 3, 2026 20:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant