Skip to content

feat(predict): improve crypto updown positions and add claim flow#30484

Merged
ghgoodreau merged 10 commits into
mainfrom
predict/crypto-updown-quality-hardening
May 21, 2026
Merged

feat(predict): improve crypto updown positions and add claim flow#30484
ghgoodreau merged 10 commits into
mainfrom
predict/crypto-updown-quality-hardening

Conversation

@ghgoodreau

@ghgoodreau ghgoodreau commented May 20, 2026

Copy link
Copy Markdown
Contributor

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

Feature: Crypto up/down price, chart, and position management

  Scenario: user views crypto up/down details
    Given the Predict feature is enabled and the user has crypto up/down positions

    When user opens the crypto up/down market details screen
    Then user sees fresh live prices across the feed card and details screen
    And expired time slots are hidden from the time-slot picker
    And the chart renders with the compact BTC Up or Down styling
    And the orderbook overlay is not shown

    When user scrolls to the positions section
    Then user sees the "Your positions" section below the chart

    When user taps Cash out on an open position
    Then the Predict cash-out flow opens for that position

    When user taps Claim on a claimable position
    Then the Predict claim action starts for claimable positions

Screenshots/Recordings

Before

N/A

After

Screenshot 2026-05-20 at 10 37 31 PM Screenshot 2026-05-20 at 10 36 23 PM

Pre-merge author checklist

Performance checks (if applicable)

  • I've tested on Android
    • Ideally on a mid-range device; emulator is acceptable
  • I've tested with a power user scenario
    • Use these power-user SRPs to import wallets with many accounts and tokens
  • I've instrumented key operations with Sentry traces for production performance metrics

For performance guidelines and tooling, see the Performance Guide.

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
  • I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.

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 positions section (new PredictCryptoUpDownPositions/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 updates usePredictPrices polling 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:ss labels, hides expired time slots from the picker, and updates the live pill styling to design-system green.

Strengthens Polymarket WebSocketManager reliability/observability with cached market price snapshots for late subscribers, per-subscriber error isolation, structured Logger.error reporting, 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.

… 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.
@github-actions

Copy link
Copy Markdown
Contributor

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
@ghgoodreau ghgoodreau marked this pull request as ready for review May 21, 2026 01:43
@ghgoodreau ghgoodreau requested review from a team as code owners May 21, 2026 01:43
@ghgoodreau ghgoodreau changed the title feat(predict): crypto updown quality hardening feat(predict): improve crypto updown positions and add claim flow May 21, 2026
@ghgoodreau ghgoodreau added the team-predict Predict team label May 21, 2026
@ghgoodreau ghgoodreau removed the request for review from a team May 21, 2026 02:51
Comment thread app/components/UI/Predict/hooks/usePredictSeriesPositions.ts
@codecov-commenter

codecov-commenter commented May 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.82759% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.16%. Comparing base (f2b07b6) to head (8140a99).

Files with missing lines Patch % Lines
...I/Predict/providers/polymarket/WebSocketManager.ts 90.21% 6 Missing and 3 partials ⚠️
...UpDownMarketCard/PredictCryptoUpDownMarketCard.tsx 83.33% 2 Missing and 1 partial ⚠️
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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ghgoodreau ghgoodreau enabled auto-merge May 21, 2026 03:41
@caieu

caieu commented May 21, 2026

Copy link
Copy Markdown
Contributor

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.

@github-actions

Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

  • Selected E2E tags: SmokePredictions, SmokeWalletPlatform, SmokeConfirmations
  • Selected Performance tags: @PerformancePredict
  • Risk Level: medium
  • AI Confidence: 92%
click to see 🤖 AI reasoning details

E2E Test Selection:
All 23 changed files are exclusively within the Predict/Polymarket feature area (app/components/UI/Predict/) plus localization strings. The changes are substantial and include:

  1. New positions display in market details: New PredictCryptoUpDownPosition and PredictCryptoUpDownPositions components show user positions inline in the Crypto Up/Down market details view, with cash out and claim buttons. This directly affects the cash-out and claim E2E flows tested by SmokePredictions.

  2. New usePredictSeriesPositions hook: Aggregates positions across a series of markets - new data fetching logic that could affect position display in E2E tests.

  3. usePredictPositions enhancement: Added marketIds batch filter option - changes position filtering logic used in existing tests.

  4. usePredictPrices polling fix: Moved polling setup to finally block so polling continues even after errors - behavioral change that affects price display reliability.

  5. useOpenOutcomes changes: Removed isMarketFetching guard (prices now fetch immediately), added pollingInterval: 2000 - changes when prices are fetched and displayed.

  6. PredictCryptoUpDownDetails major update: Dynamic chart sizing based on positions presence, filtered time slots to non-ended markets only, integrated positions display.

  7. PredictCryptoUpDownChart changes: Changed price format to whole dollars (no decimals), removed orderbook display, removed direction momentum badge - visual changes that could affect screenshot/assertion tests.

  8. TimeSlotPicker color changes: Live indicator changed from error-red to success-green - visual change.

  9. WebSocketManager improvements: Added heartbeat monitoring for stale connections, improved error logging with structured context, added price caching for new subscribers, fixed reconnect logic to prevent duplicate reconnects.

  10. New test IDs and localization strings: Added for positions section.

Tag selection rationale:

  • SmokePredictions: Primary tag - all changes are in the Predict feature. The positions display, cash out, and claim flows are directly affected.
  • SmokeWalletPlatform: Required per SmokePredictions tag description: "Predictions is also a section inside the Trending tab (SmokeWalletPlatform); changes to Predictions views affect Trending. When selecting SmokePredictions, also select SmokeWalletPlatform."
  • SmokeConfirmations: Required per SmokePredictions tag description: "When selecting SmokePredictions, also select SmokeConfirmations (opening/closing positions are on-chain transactions)."

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:
The changes include WebSocket heartbeat monitoring, price polling fixes (usePredictPrices now polls continuously even after errors), and new position data fetching (usePredictSeriesPositions with multiple useQueries calls). These changes could impact the performance of the Predict market loading and position display flows. The @PerformancePredict tag covers prediction market list loading, market details, deposit flows, and balance display - all of which are affected by the WebSocket and polling changes.

View GitHub Actions results

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ 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.

@sonarqubecloud

Copy link
Copy Markdown

@ghgoodreau ghgoodreau added this pull request to the merge queue May 21, 2026
Merged via the queue into main with commit 386f887 May 21, 2026
196 of 199 checks passed
@ghgoodreau ghgoodreau deleted the predict/crypto-updown-quality-hardening branch May 21, 2026 15:29
@github-actions github-actions Bot locked and limited conversation to collaborators May 21, 2026
@metamaskbotv2 metamaskbotv2 Bot added the release-7.79.0 Issue or pull request that will be included in release 7.79.0 label May 21, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-7.79.0 Issue or pull request that will be included in release 7.79.0 size-XL team-predict Predict team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants