Skip to content

fix(api): stop fabricating markPrice/indexPrice in live WS price ticks [BUG-101] - #218

Open
Morenikeoa wants to merge 1 commit into
dcccrypto:mainfrom
Morenikeoa:fix/oracle-broadcaster-price-fields
Open

fix(api): stop fabricating markPrice/indexPrice in live WS price ticks [BUG-101]#218
Morenikeoa wants to merge 1 commit into
dcccrypto:mainfrom
Morenikeoa:fix/oracle-broadcaster-price-fields

Conversation

@Morenikeoa

@Morenikeoa Morenikeoa commented Jun 26, 2026

Copy link
Copy Markdown

Problem

OraclePriceBroadcaster hardcoded markPriceE6/indexPriceE6 to the same value as the single oracle push price on every oracle_prices INSERT. The oracle_prices table genuinely carries only one price per row — there's no separate mark/index price in this event source. ws.ts's flushPriceUpdate forwards these fabricated values unchanged to every live WS subscriber.

Impact

Every live WS price tick reported markPrice === indexPrice === oracle price, directly contradicting the correct, distinct values the same channel sends from market_stats on initial subscribe. On markets where mark/index price legitimately diverges from the oracle price (basis, funding skew), this is a real-time data-integrity bug, not just staleness — a client's first message after subscribing can show markPrice != indexPrice, then the very next live tick silently collapses them to equal.

Fix

Stop publishing markPriceE6/indexPriceE6 from this source entirely. flushPriceUpdate (src/routes/ws.ts) already has a markPriceE6 ? ... : undefined check specifically meant to omit these fields from the outgoing WS message when not genuinely known — it was just never reachable because the broadcaster always supplied a (wrong) value. No change needed in ws.ts: live ticks now correctly omit markPrice/indexPrice rather than reporting a fabricated one, while the initial-subscribe snapshot continues to send the correct, distinct values from market_stats.

I considered the alternative of having the broadcaster also query market_stats for genuine mark/index price on every oracle push, but that adds a DB round-trip to a high-frequency event path for values that update on a much slower cadence (crank-driven, not oracle-push-driven) — omitting the fields when not known is the more honest and lower-risk fix.

Proof of Fix

New tests assert the published payload never carries markPriceE6/indexPriceE6, and that non-positive/non-finite prices are still correctly ignored.

Verified this is a genuine regression test: reverted just the source change and reran — failed with markPriceE6: 1500000 present where it should be absent. Restored the fix and it passes.

  • All existing tests pass — output attached.
  • New tests pass against the fix, the core one fails against pre-fix code (verified locally).
  • tsc --noEmit clean (no separate lint script in this repo).

Test Output

✓ tests/services/oracle-price-broadcaster.test.ts (2 tests) 5ms

Full suite: 296/297 passed (294 baseline + 2 new). The 1 failure (tests/sdk-smoke.test.ts) is pre-existing and unrelated — it asserts on an exact @percolatorct/sdk error-message string that has drifted from the locally-resolved SDK version in this environment.

Related

Found during a broader API audit; no existing open issue/PR covers this.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed oracle price updates so they now publish only the incoming price value, avoiding incorrect duplicate mark/index prices.
    • Improved handling of invalid oracle prices by ignoring zero, non-numeric, or otherwise unusable values.
  • Tests

    • Added regression coverage for oracle price broadcasts to confirm the correct event payload and filtering behavior.

…s [BUG-101]

OraclePriceBroadcaster hardcoded markPriceE6 and indexPriceE6 to the same
value as the single oracle push price on every oracle_prices INSERT,
because that table only carries one price per row — there's no genuine
mark/index price in this event source to report. ws.ts's flushPriceUpdate
forwarded these fabricated values unchanged to every live WS subscriber,
so every live price tick reported markPrice === indexPrice === oracle
price, directly contradicting the correct, distinct values the same
channel sends from market_stats on initial subscribe. On markets where
mark/index price legitimately diverges from the oracle price (basis,
funding skew), this was a real-time data-integrity bug, not just
staleness — a client's first message after subscribing could show
markPrice != indexPrice, then the very next live tick collapses them.

Stop publishing markPriceE6/indexPriceE6 from this source entirely.
flushPriceUpdate already has a `markPriceE6 ? ... : undefined` check
(ws.ts) specifically meant to omit these fields when not genuinely known
— it was just never reachable because the broadcaster always supplied a
(wrong) value. No change needed in ws.ts; live ticks now correctly omit
markPrice/indexPrice rather than reporting a fabricated one.

Added tests verifying the published payload never carries
markPriceE6/indexPriceE6, and that non-positive/non-finite prices are
still ignored. Verified the test fails against the pre-fix code (received
markPriceE6: 1500000 where it should be absent) and passes against the fix.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 26, 2026

Copy link
Copy Markdown

@Princessdada is attempting to deploy a commit to the Khubair Nasir's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0152d701-811a-40c8-bc9e-b26a9a9b9ea9

📥 Commits

Reviewing files that changed from the base of the PR and between b2751f4 and 41bcf8a.

📒 Files selected for processing (2)
  • src/services/OraclePriceBroadcaster.ts
  • tests/services/oracle-price-broadcaster.test.ts

📝 Walkthrough

Walkthrough

Oracle price inserts now publish price.updated events with only the incoming price_e6 mapped to priceE6. The broadcaster no longer fabricates markPriceE6 or indexPriceE6 from the same value, and regression tests cover the payload shape and invalid inputs.

Changes

Oracle price publish payload

Layer / File(s) Summary
Publisher payload update
src/services/OraclePriceBroadcaster.ts
The oracle_prices insert handler publishes price.updated with only priceE6 and adds a comment describing the single-price mapping.
Regression coverage
tests/services/oracle-price-broadcaster.test.ts
The Vitest suite mocks the broadcaster dependencies, captures the realtime insert callback, and checks the emitted payload and invalid price_e6 inputs.

Estimated review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • dcccrypto/percolator-api#178: Revises the same OraclePriceBroadcaster oracle_prices insert path and removes the fabricated markPriceE6/indexPriceE6 fields.

Poem

🐇 I hop where oracle prices gleam,
One push, one price, a tidy stream.
No borrowed marks, no borrowed lines,
Just honest hops and test-time signs.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main fix: removing fabricated markPrice/indexPrice values from live WebSocket price ticks.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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