feat(predict): improve crypto updown positions and add claim flow#30484
Conversation
… and details The crypto up/down feed card was showing stale ~50/51¢ seed values while the details screen showed accurate live prices. Polymarket's market WebSocket sends sparse price_change events for these synthetic markets, so the feed card's WS-only price path got starved. The details screen already had a REST source (usePredictPrices) as a fallback; the feed card did not. Feed card price path: - OutcomeButtons now polls usePredictPrices alongside useLiveMarketPrices, mirroring useOpenOutcomes. Precedence is now live WS bestAsk > REST sell > seed token.price. Details screen price path: - useOpenOutcomes drops the !isMarketFetching gate on both the WS subscription and the REST query so neither tears down during the 10s market refetch cycle. - REST polling at 2s acts as a continuous freshness floor on both feed card and details. usePredictPrices hardening: - Schedule the next poll in finally so transient network errors do not permanently kill the floor. - Preserve last-good prices on error instead of clearing them, so consumers do not flash blank. WebSocketManager hardening (Tier 1 work uncovered while debugging): - Wrap market price subscriber callbacks in try/catch + Logger.error to stop a single throwing subscriber from freezing peers (mirrors the RTDS path). - Promote DevLogger to Logger.error with structured tags+context for connection failures, parse failures, onerror handlers, and callback failures across sports/market/RTDS channels. - Reconnect mutex with first-wins semantics: duplicate onclose events no longer burn attempt budget; attempts only increment when a scheduled timeout actually fires. - Add a marketPriceCache that writes through every message and is served synchronously to new subscribers, eliminating blank-on-mount. Cleared on disconnect, last-unsubscribe, and app background. - Heartbeat timeout: track lastMessageReceivedAt per channel and force a reconnect when stale (60s market / 15s RTDS, 5s check cadence) so silently-dead sockets do not linger. Tests: 102 WebSocketManager tests (24 new across callback isolation, observability, reconnect mutex, market price cache, heartbeat staleness, cache invalidation, attempt-budget preservation), plus updated usePredictPrices coverage for polling-after-error and last-good preservation.
…ker scrollable
Two small UX fixes for the crypto up/down details screen ahead of ship:
- Filter expired slots out of the TimeSlotPicker on the details screen.
Tapping a past slot used to either flash empty data or auto-revert to
the live slot (Polymarket's /chainlink-candles only returns the most
recent N candles, so past slots older than the variant's lookback
show no data). Until the past-slot chart UX lands, surfacing those
pills as pressable is misleading, so they are simply hidden via
hasMarketEnded(). The auto-advance / rollover logic still operates
on the unfiltered currentSeriesMarkets so live transitions keep
working.
- Stop the TimeSlotPicker from yanking the scroll position back to the
selected pill on every parent re-render. The new visibleSlotMarkets
filter is memoised in the details screen so its array reference is
stable when the content is, and the picker's autoscroll effect now
depends only on resolvedSelectedId. Initial mount, user taps, and
rollover-driven selection changes still trigger an autoscroll; idle
parent re-renders (countdown ticks, chart updates, etc.) no longer
do. This unblocks tapping a future slot and then scrolling back to
live without the picker fighting the user.
Test changes: repurposed the existing 'auto-advances to the live
market when an expired slot is selected' test into one that asserts
expired pills are filtered from the picker entirely; it now uses
queryByTestId(...).toBeNull() instead of pressing an expired pill.
Full historical chart and sticky past-slot navigation work for the
proper past-mode UX is stashed locally (see stash@{0}) for follow-up.
|
CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes. |
…-quality-hardening # Conflicts: # app/components/UI/Predict/components/PredictCryptoUpDownDetails/PredictCryptoUpDownDetails.tsx # app/components/UI/Predict/providers/polymarket/WebSocketManager.ts
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #30484 +/- ##
==========================================
+ Coverage 82.15% 82.16% +0.01%
==========================================
Files 5479 5482 +3
Lines 147496 147697 +201
Branches 33910 33941 +31
==========================================
+ Hits 121172 121354 +182
- Misses 18026 18038 +12
- Partials 8298 8305 +7 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Finding from codex review: [P1] Inline claim bypasses the Predict eligibility/geoblock guard in PredictCryptoUpDownPosition.tsx (line 217). The existing details-level Claim CTA wraps claim() with executeGuardedAction(... CLAIM ...) in PredictMarketDetails.tsx (line 278), but this new row button calls claim() directly. usePredictClaim does not perform that guard itself, so an ineligible/geoblocked user with a claimable crypto up/down position can enter the claim confirmation flow from the inline row. I’d route this through a guarded claim handler and add a regression test for isEligible=false. |
🔍 Smart E2E Test Selection
click to see 🤖 AI reasoning detailsE2E Test Selection:
Tag selection rationale:
No other tags are needed as changes are isolated to the Predict feature with no impact on other wallet flows, network management, accounts, swaps, browser, or snaps. Performance Test Selection: |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit b521867. Configure here.
|




Description
This PR hardens the Predict crypto up/down experience. It keeps feed-card and details-screen prices fresh by pairing live Polymarket prices with REST polling fallback, hides expired time slots from the details picker, adds a series-aware "Your positions" section, and renders inline cash-out / claim actions for positions in the selected series.
It also polishes the BTC Up or Down chart and live UI by removing the oversized price badge, tightening the chart presentation, formatting y-axis values as whole dollars, using compact live-window time labels, applying the design-system green to the live control, and temporarily hiding the orderbook overlay until the live duplicate-order issue is resolved.
Changelog
CHANGELOG entry: Improved crypto up/down price freshness, chart polish, and position management in Predict
Related issues
Fixes: N/A - follow-up hardening for Predict crypto up/down.
Manual testing steps
Screenshots/Recordings
Before
N/A
After
Pre-merge author checklist
Performance checks (if applicable)
trace()for usage andaddTokenfor an exampleFor performance guidelines and tooling, see the Performance Guide.
Pre-merge reviewer checklist
Note
Medium Risk
Touches Predict trading UX and real-time price plumbing (REST polling fallbacks, WebSocket reconnect/heartbeat, and new cash-out/claim UI), which can impact price freshness and position actions if incorrect. Changes are well-covered by new/updated unit tests but still warrant careful QA on device/network edge cases.
Overview
Improves the crypto up/down details experience by adding a series-aware
Your positionssection (newPredictCryptoUpDownPositions/PredictCryptoUpDownPosition) that shows per-position PnL with inline Cash out (open) and Claim (claimable resolved) actions, and updates the details screen layout to shrink the chart when positions exist.Hardens price freshness by combining live WebSocket best-ask with REST polling fallbacks in the crypto up/down feed card and
useOpenOutcomes, and updatesusePredictPricespolling to continue after transient errors while preserving last-good results.Polishes the crypto up/down chart and picker: disables the Liveline badge/momentum and temporarily disables the orderbook overlay, formats chart values as whole डॉलर with compact
h:mm:sslabels, hides expired time slots from the picker, and updates the live pill styling to design-system green.Strengthens Polymarket
WebSocketManagerreliability/observability with cached market price snapshots for late subscribers, per-subscriber error isolation, structuredLogger.errorreporting, heartbeat-based stale-connection reconnects, and reconnect scheduling that avoids stacking timeouts.Reviewed by Cursor Bugbot for commit b521867. Bugbot is set up for automated code reviews on this repo. Configure here.