fix(predict): fix claim button not showing and not working for resolved positions#27050
fix(predict): fix claim button not showing and not working for resolved positions#27050vinnyhoward wants to merge 6 commits into
Conversation
|
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. |
| // Get claimable positions from state | ||
| const claimablePositions = this.state.claimablePositions[signer.address]; | ||
| // Get claimable positions from state (case-insensitive address match) | ||
| const normalizedSignerAddress = signer.address.toLowerCase(); |
There was a problem hiding this comment.
Can we create a utils function for this logic? This function can be shared for the controller and the selector.
…mcu-perps-claim-not-working-homepage
🔍 Smart E2E Test Selection
click to see 🤖 AI reasoning detailsE2E Test Selection:
Tag Selection Rationale:
The changes are well-scoped to the Predict feature with comprehensive unit tests added. Risk is medium because while the changes affect a controller (critical file pattern), they are isolated to the Predict domain and don't cascade to other controllers. 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.
| jest.clearAllMocks(); | ||
| mockClaim.mockResolvedValue(undefined); | ||
| mockRefreshClaimable.mockResolvedValue(undefined); | ||
| mockClaim.mockResolvedValue({ wasCancelled: false }); |
There was a problem hiding this comment.
Test mock omits succeeded field, hiding untested code path
Medium Severity
The mockClaim mock resolves with { wasCancelled: false } but omits the succeeded field that handleClaim destructures. This means succeeded is always undefined in tests, so setHasClaimed(true) is never called, and the PR's key new optimistic-hide behavior for the claim button is entirely untested. The mock needs to include succeeded: true to exercise the production code path.
Additional Locations (1)
| const hasError = | ||
| !isLoadingPositions && | ||
| !isLoadingMarkets && | ||
| !isLoading && |
There was a problem hiding this comment.
Section analytics incorrect when only claimable positions exist
Low Severity
The rendering condition was expanded to show the positions branch when totalClaimable > 0 (even without active positions), but hasPositions, isEmpty, itemCount, and willRender still only consider active (non-claimable) positions. When only claimable positions exist, the section renders the claim button but isEmpty is true, willRender is false, and itemCount is 0, causing incorrect analytics tracking via useHomeViewedEvent.
Additional Locations (1)
The committed fixture schema is out of date. To update, comment: |
|
| }); | ||
| await claimWinnings({}); | ||
| const result = await claimWinnings({}); | ||
| return { |
There was a problem hiding this comment.
We weren’t using the result before. Since we’re using it now, instead of returning flags, let’s return the result object itself so whoever consumes it can use it however they need.
| private getClaimablePositionsByAddress( | ||
| address: string, | ||
| ): { positions: PredictPosition[]; matchedKey: string } | undefined { | ||
| const matchedKey = findAddressKey(this.state.claimablePositions, address); | ||
| if (!matchedKey) return undefined; | ||
| return { positions: this.state.claimablePositions[matchedKey], matchedKey }; | ||
| } | ||
|
|
There was a problem hiding this comment.
We should not need this change. Also, we now use React Query for our positions queries and you should be reusing our hooks (or at least our queries) rather than creating custom fetch logic.
Let's have a sync on this if needed. We're happy to help fix the issues across the Predict section in the new homepage.
|
Issue has been fixed in main and now closing this PR |





Description
Fixed several bugs with the Predict claim button on the homepage that prevented users from seeing and successfully claiming winnings from resolved markets.
Claim failed with "No claimable positions found" —
PredictController.claimWithConfirmationused a direct key lookup (state.claimablePositions[signer.address]) to find stored positions. The address key was stored using checksummed format but read back with different casing, causing the lookup to silently return undefined. Fixed with case-insensitive address matching, consistent with the existing pattern inconfirmClaim. The duplicated address-lookup logic in bothclaimWithConfirmationandconfirmClaimwas extracted into a privategetClaimablePositionsByAddresshelper.Claim button didn't disappear after successful claim on homepage —
claimWithConfirmationresolves on transaction submission (not on-chain confirmation), sorefreshPositions()fired while the API still returned the position as claimable. Fixed with an optimistichasClaimedflag that immediately hides the button on submission, and_clearPositionsCache()called fromusePredictToastRegistrationswhen the claim transaction is confirmed, so the next visit fetches fresh data.Simplified positions fetch in
PredictionsSection— The hook was called twice: once to fetch active positions and again withclaimable: trueto fetch claimable positions, resulting in two separate API calls to Polymarket's/positionsendpoint. The second call used aredeemable=truefilter that returns positions at unpredictable times relative to on-chain settlement, which is why pull-to-refresh missed winning positions while navigating into a position detail (which used an unfiltered React Query call) and back would reveal them. NowusePredictPositionsForHomepageis called once with no filter, fetching all positions, and the result is split client-side using position.claimable.Changelog
CHANGELOG entry:null
Related issues
Fixes:
Manual testing steps
Screenshots/Recordings
~Before
Simulator.Screen.Recording.-.iPhone.16e.-.2026-03-05.at.16.27.41.mov
After
Simulator.Screen.Recording.-.iPhone.16e.-.2026-03-06.at.14.24.40.mov
Pre-merge author checklist
Pre-merge reviewer checklist
Note
Medium Risk
Touches the claim flow and homepage positions caching/rendering; mistakes could hide claimable winnings or prevent claims from being triggered, but changes are localized and covered by added tests.
Overview
Fixes Predict claiming and homepage display issues caused by address-casing mismatches and stale/fragmented position fetching.
PredictControllernow resolvesclaimablePositionsvia a shared case-insensitive lookup (findAddressKey/getClaimablePositionsByAddress) for bothclaimWithConfirmationandconfirmClaim, and selectors are updated to read claimable positions case-insensitively.Homepage Predictions now fetches all positions in one call (
usePredictPositionsForHomepagedrops the claimable filter and unifies its cache key), splits claimable vs active client-side, and hides the claim button optimistically after submission (usePredictClaimreturns{succeeded, wasCancelled}plushasClaimed/isClaimingstate). On tx confirmation,usePredictToastRegistrationsclears the homepage positions cache to force a fresh fetch next visit/refresh. Tests were updated/added to cover these scenarios.Written by Cursor Bugbot for commit 9d40ff2. This will update automatically on new commits. Configure here.