chore(runway): cherry-pick feat(predict): Implement Predict Bet Slip PRED-707 cp-7.74.0#28955
Merged
Merged
Conversation
…PRED-707 cp-7.74.0 (#28779) ## **Description** Migrate the Predict Buy and Sell preview screens from full-screen stack navigation into BottomSheet wrappers, gated behind the `predictBottomSheet` LaunchDarkly feature flag. When the flag is OFF, the existing full-screen navigation flow is preserved with zero behavioral changes. ### What changed **New: `PredictPreviewSheet` wrapper component** - Generic BottomSheet wrapper that renders buy/sell preview content as children. - Uses `BottomSheet`, `BottomSheetHeader`, and `BottomSheetHeaderVariant` from `@metamask/design-system-react-native` (not the deprecated component-library version). - Supports `renderHeader` prop for custom header content (sell sheet uses it for rich cashout info); falls back to default icon+title+subtitle layout (buy sheet). - `isFullscreen` prop controls sheet height: content-fitted for both buy and sell sheets (auto-sizes to content). - Title and subtitle elements expose `testID` props (`preview-sheet-title`, `preview-sheet-subtitle`) for testID-based assertions. - Unit tests for the wrapper component. **New: `PredictPreviewSheetContext` (central sheet state manager)** - Context provider manages both buy and sell sheet refs, params, and open/close lifecycle. - Exposes `openBuySheet()` / `openSellSheet()` methods consumed by all card components. - Feature flag check determines whether to navigate (old flow) or open a sheet (new flow). - `SellSheetHeader` extracted as a standalone component within the context for the rich header. - Graceful fallback: `usePredictPreviewSheet()` returns navigation-based routing when used outside the provider (e.g. home carousel, trending feed) instead of throwing, with memoized references to prevent unnecessary re-renders. - Nonce-based re-open mechanism to handle identical params across consecutive sheet opens. - Unit tests for the context provider. **New: `predictBottomSheet` feature flag** - Registered in `feature-flag-registry.ts` as a remote, version-gated flag (default: disabled). - New selector `selectPredictBottomSheetEnabledFlag` in `selectors/featureFlags/index.ts`. **New: `PredictQuickAmounts` component (with haptic feedback)** - Standalone quick-pick buttons ($20, $50, $100, $250) displayed in the bottom content area of the buy sheet. - Uses `expo-haptics` (`impactAsync` with `ImpactFeedbackStyle.Light`) for tactile feedback on button press. `impactAsync` is properly awaited with error swallowing for unsupported devices. - Quick amounts set the input to whole numbers (e.g. `"20"` not `"20.00"`) for easier editing. - Disabled buttons are verified non-functional in tests. - Uses design-system `Box` component instead of raw `View`. - Unit tests including haptic feedback verification and async handler coverage. **New: `usePredictCashOut` hook (shared cashout logic)** - Encapsulates the guarded cashout action flow: find outcome by `position.outcomeId`, call `openSellSheet`, and handle errors with `Logger.error` + toast notification. - Accepts `market` and `callerName` parameters; `callerName` is included in error metadata for production diagnostics. - Consumed by both `PredictPicks` and `PredictPositionDetail`, eliminating ~40 lines of identical code from each. - Exported from `hooks/index.ts`. - Unit tests covering guarded action invocation, `openSellSheet` params, error/toast on missing outcome, and `callerName` in metadata. **Navigation migration (10 components updated)** All components that previously called `navigateToBuyPreview()` / `navigate(Routes.PREDICT.MODALS.SELL_PREVIEW)` now call `openBuySheet()` / `openSellSheet()` from `usePredictPreviewSheet`: - `PredictMarketDetails` - outcome buy actions - `PredictMarketSingle` - Yes/No buy buttons - `PredictMarketMultiple` - outcome buy buttons - `PredictMarketOutcome` - price buttons - `PredictSportCardFooter` - feed card buy actions - `FeaturedCarouselCard` - carousel card outcome buy buttons - `FeaturedCarouselSportCard` - sport carousel card buy buttons - `PredictPicks` - cashout from picks list via shared `usePredictCashOut` hook - `PredictPositionDetail` - cashout from position detail via shared `usePredictCashOut` hook - Removed direct dependency on `usePredictNavigation` from all these components. **Routes (`routes/index.tsx`)** - Wrapped the stack navigator with `PredictPreviewSheetProvider` so sheets are available throughout the Predict navigation tree. **Buy sheet (`PredictBuyWithAnyToken`)** - Uses discriminated union props (`mode: 'sheet' | 'screen'`) for type-safe sheet vs navigation mode, replacing `Partial<ContentProps>` with `as` casts. - Sheet mode layout: header hidden (provided by `PredictPreviewSheet`), `PredictQuickAmounts` in bottom content, `PredictPayWithRow` in compact `variant="row"` mode with balance display, keypad moved below bottom content with `hideHeader`, border hidden, action button displays "Confirm". - Uses `Box` wrapper instead of `SafeAreaView` in sheet mode. - `PredictPayWithAnyTokenInfo` receives `isInputFocused: false` in sheet mode to ensure mm_pay relay configuration runs immediately (prevents underfunded deposit). - Sheet cleanup: unmount runs `onReject` + `clearActiveOrderTransactionId` (matches `beforeRemove` behavior in old flow). - Approval request recovery: when `approvalRequest` is missing during `handleConfirm` in PAY_WITH_ANY_TOKEN state, attempts re-initialization via `initPayWithAnyToken()` (clears `batchIdRef`, rejects pending transactions, re-creates the approval) before returning `PLACE_ORDER_FAILED`. This gives the user a chance to retry with a fresh approval rather than silently failing. **Buy sheet (`PredictBuyPreview` - legacy non-pay-with-any-token)** - Same discriminated union props / `isSheetMode` pattern for BottomSheet compatibility. - Header and back button hidden in sheet mode; close action routed to `onClose()`. - Uses `ScrollView` from `react-native-gesture-handler` (aliased as `GHScrollView`) in sheet mode to cooperate with the BottomSheet's `PanGestureHandler`; standard React Native `ScrollView` preserved for the full-screen path. **Sell sheet (`PredictSellPreview`)** - Same discriminated union props pattern; `isSheetMode` drives conditional layout: icon+title row hidden in sheet mode (rendered by `SellSheetHeader` in the sheet header instead), price/shares/PnL section relocated to the bottom area. - Sheet mode uses inline Tailwind `tw.style('flex-col')` instead of `StyleSheet.create()` for the container. - Extracted `getCashoutInfoText` helper into `utils/format.ts` to DRY up the localized cashout info string. **`PredictController` (batch transaction fix)** - Added `gasFeeToken: MATIC_CONTRACTS.collateral` to the `initPayWithAnyToken` batch submission, matching other batch calls. This ensures mm_pay configures the relay step to bridge funds when the Safe has insufficient USDC balance. **`PredictKeypad`** - New `hideHeader` prop to suppress the quick-amount row + Done button when rendered inside the buy sheet (avoids duplication with `PredictQuickAmounts`). **`PredictPayWithRow`** - New `variant="row"` mode: compact single-row layout showing token icon, symbol, balance, and right chevron for the sheet. - New `availableBalance` prop for displaying balance in row mode. **`PredictBuyAmountSection`** - New `hideAvailableBalance` prop to suppress the "Available: $X.XX" text in sheet mode. **`PredictBuyBottomContent`** - New `hideBorder` prop to remove the top border separator in sheet mode. - Restored inline text wrapping for the disclaimer + "Learn more" link (nested `Text` instead of sibling flex items). **`PredictBuyActionButton`** - New `isSheetMode` prop: when true, displays "Confirm" label instead of "{outcome} · {price}". **`PredictFeeSummary`** - Removed "incl. fees" subtitle text; moved info icon inline next to "Total" on the same line. - Total amount typography changed from `HeadingMd`/`Bold` to `BodyMd`/`Medium` to match Pay With row. **Bug fixes** - `usePredictBuyConditions`: Auto-revert effect that switched back to Predict balance now requires `totalPayForPredictBalance > 0`, preventing immediate revert when the amount is 0 (which always caused the payment method selection to snap back in sheet mode). - `usePredictBuyConditions`: `shouldWaitForPayFees` now also requires `currentValue > 0`. - `usePredictBuyActions`: When `approvalRequest` is missing during `handleConfirm` in PAY_WITH_ANY_TOKEN state, attempts re-initialization (`initPayWithAnyToken()`) before returning early with error, giving the user a retry path rather than silently failing. Removed dead fallback branch that was unreachable (transactionId derived from the same approvalRequest). - `usePredictBuyActions`: `batchIdRef` cleared at the start of `doInit()` to prevent stale values between transaction attempts. - `usePredictBuyActions`: Sheet unmount cleanup mirrors `beforeRemove` behavior from the old flow. - `usePredictBuyActions`: DEPOSITING and SUCCESS state effects check `isSheetMode` to call `onClose()` instead of `StackActions.pop()`. - `usePredictBuyError`: Removed `isInputFocused` from the `blockingPayAlertMessage` gate. The flag was permanently suppressing insufficient-funds errors in sheet mode because `isInputFocused` initialized to `true` and never transitioned to `false` when using quick-amount buttons. The `isPayFeesLoading` check already handles stale-data concerns. - `PredictPicks` / `PredictPositionDetail`: Cashout logic (outcome lookup, `openSellSheet`, error handling with `Logger.error` + toast) extracted into shared `usePredictCashOut` hook to eliminate duplication. - `PredictPayWithAnyTokenInfo`: `isInputFocused` overridden to `false` in sheet mode so `updatePendingAmount` and `setPayToken` run immediately, ensuring mm_pay relay step is configured before confirmation. - `PredictController`: Added `gasFeeToken` to `initPayWithAnyToken` batch to fix missing mm_pay relay step that caused "Insufficient USDC balance in Safe" errors. - `PredictPreviewSheetContext`: Stabilized `onDismiss` callbacks with `useCallback` to prevent unstable `closeSheet`/`onClose` references from causing repeated SUCCESS/DEPOSITING effect re-fires. **Code quality** - Extracted shared `usePredictCashOut` hook to deduplicate identical `onCashOut` logic (~40 lines each) from `PredictPicks` and `PredictPositionDetail`. Both components now use a single hook call instead of duplicating the guarded action, outcome lookup, `openSellSheet` call, and error handling with `Logger.error` + toast. The `callerName` parameter preserves per-component error metadata. - Replaced raw `View` with design-system `Box` in `PredictQuickAmounts`. - Removed stale comments from `usePredictBottomSheet`. - Discriminated union types (`PredictBuyPreviewProps`, `PredictSellPreviewProps`) replace `Partial<ContentProps>` with `as` casts, providing compile-time safety for sheet vs navigation mode. - New test IDs: `BUY_PREVIEW_SHEET`, `SELL_PREVIEW_SHEET`. - Memoized fallback return value in `usePredictPreviewSheet` to prevent unstable function references. - Memoized `BuyComponent` selection in `PredictPreviewSheetContext` with `useMemo` to prevent unnecessary unmount/remount cycles. - Added `index.ts` barrel export for `PredictPreviewSheet` component (file organization standard). - Restored slide-from-right `cardStyleInterpolator` on `BUY_PREVIEW` and `SELL_PREVIEW` stack screens for the flag-OFF full-screen path. - Replaced `require()` with `jest.requireActual()` in all test mock factories. - Fixed module-level shared mutable state (`navigationRef`) in `routes/index.test.tsx` — moved to `beforeEach`. - Strengthened test assertions in `PredictMarketDetails.test.tsx` to verify exact call parameters with `expect.objectContaining()`. - Removed dead/unused `predict.order.buy` i18n key and its stale test mock reference. - Migrated `PredictPreviewSheet` from deprecated component-library `BottomSheet` to `@metamask/design-system-react-native` `BottomSheet`/`BottomSheetHeader` (DS-first UI rule). Updated `usePredictBottomSheet` hook to import `BottomSheetRef` from DS. - Replaced `shouldNavigateBack={false}` with DS equivalent (omit `goBack` prop); changed header `style` prop to `twClassName`. - `PredictBuyPreview`: Uses gesture-handler-aware `ScrollView` from `react-native-gesture-handler` in sheet mode to prevent scroll/dismiss conflicts with the BottomSheet's `PanGestureHandler` on Android. - Replaced all `toBeNull()` assertions with `not.toBeOnTheScreen()` in test files per unit-test guidelines. - Replaced text-based queries (`getByText`, `queryByText`) with testID-based assertions (`getByTestId` + `toHaveTextContent`) in `PredictPreviewSheet.test.tsx` and `PredictPreviewSheetContext.test.tsx`. - Changed synchronous `act()` to `await act(async () => { ... })` in `routes/index.test.tsx` to flush async navigator state updates and eliminate act() warnings. **Localization** - Added `predict.odds` key ("Odds"). - Added `predict.order.confirm` key ("Confirm") for the sheet action button. - Removed unused `predict.order.buy` key ("Buy {{outcome}}"). **Documentation** - Added comprehensive Predictions architecture guide (`docs/predict/predictions-comprehensive-guide.md`). - Added ticket documentation (`docs/predict/tickets/buy-sell-bottomsheet-migration.md`). ## **Changelog** CHANGELOG entry: null ## **Related issues** Refs: [PRED-707](https://consensyssoftware.atlassian.net/browse/PRED-707?atlOrigin=eyJpIjoiZGU2Y2YwN2RlMWJjNDZiNmJkYjdkMmQwMzQ2ZDFmZmMiLCJwIjoiaiJ9) ## **Manual testing steps** ```gherkin Feature: Predict Buy/Sell BottomSheet Migration Scenario: Buy prediction via bottom sheet (flag ON) Given the predictBottomSheet feature flag is enabled And the user is on a prediction market details page When user taps a "Yes" or "No" outcome button Then a bottom sheet opens with the buy preview content And the sheet header shows the outcome icon, "Yes/No · Outcome Name" title, and odds subtitle And quick amount buttons ($20, $50, $100, $250) are visible with haptic feedback And the user can enter an amount, select payment method, and tap "Confirm" And the order is placed successfully And the sheet closes after the deposit is initiated Scenario: Sell position via bottom sheet (flag ON) Given the predictBottomSheet feature flag is enabled And the user has an active position on a market When user taps "Cash out" on their position Then a content-fitted bottom sheet opens (not fullscreen) And the sheet header shows the position icon, title, and cashout info And the body shows the current value, shares, PnL, and Cash out button And tapping "Cash out" places the sell order and closes the sheet Scenario: Buy prediction via full-screen navigation (flag OFF) Given the predictBottomSheet feature flag is disabled And the user is on a prediction market details page When user taps a "Yes" or "No" outcome button Then the app navigates to the full-screen BuyPreview screen And the existing layout and behavior are unchanged Scenario: Sell position via full-screen navigation (flag OFF) Given the predictBottomSheet feature flag is disabled And the user has an active position on a market When user taps "Cash out" on their position Then the app navigates to the full-screen SellPreview screen And the existing layout and behavior are unchanged Scenario: Change payment method in buy sheet (flag ON) Given the predictBottomSheet feature flag is enabled And the buy bottom sheet is open When user taps the Pay with row and selects a different token (e.g. USDC) Then the payment method updates and stays selected And the fee summary and total update accordingly Scenario: Buy from feed cards and carousel (flag ON) Given the predictBottomSheet feature flag is enabled And the user is on the Predict feed or homepage carousel When user taps a Yes/No button on any market card Then the bottom sheet opens with the correct market/outcome pre-filled And the flow works identically to opening from market details Scenario: Components outside PredictScreenStack (flag ON) Given the predictBottomSheet feature flag is enabled And a Predict card is rendered on the homepage carousel (outside PredictScreenStack) When user taps a Yes/No button Then the app navigates via React Navigation (fallback behavior) And no crash occurs from missing PredictPreviewSheetProvider Scenario: Quick amount haptic feedback (flag ON) Given the buy bottom sheet is open When user taps a quick amount button ($20, $50, $100, or $250) Then haptic feedback fires (Light impact) And the amount input updates to the selected value And the input unfocuses ``` ## **Screenshots/Recordings** ### **Before** Full-screen buy/sell preview navigation (flag OFF) - no visual changes to this flow. ### **After** https://github.com/user-attachments/assets/1e365eeb-74bf-4281-a415-0453606c5d80 ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [x] 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. [PRED-707]: https://consensyssoftware.atlassian.net/browse/PRED-707?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches Predict trade entry points and preview/cash-out flows, plus introduces a new feature-flagged UI layer; regressions could affect order preview UX or sheet/navigation behavior, though the legacy route path remains as fallback. > > **Overview** > Adds a new feature-flagged bet slip flow for Predict by introducing `PredictPreviewSheet` + `PredictPreviewSheetProvider`/`usePredictPreviewSheet`, allowing buy/sell previews to open in BottomSheets (with navigation fallback when the provider/flag isn’t available). > > Updates multiple Predict entry points (feed cards, carousel cards, market single/multiple/outcome components, and sport footer) to call `openBuySheet` instead of navigating to the buy preview, and refactors cash-out handling into a shared `usePredictCashOut` hook that opens the sell sheet and handles missing-outcome errors via logging + toast. > > Extends buy preview screens (`PredictBuyPreview`, `PredictBuyWithAnyToken`) to support a new `mode: 'sheet'` rendering path (layout tweaks, keypad/header behavior, confirm CTA), adds new test IDs/feature-flag selectors (`predictBottomSheet`), and adjusts provider order result shape and assorted tests to match the new behavior. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit ca83caf. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Caainã Jeronimo <caainaje@gmail.com> Co-authored-by: Luis Taniça <matallui@gmail.com>
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. |
Contributor
🔍 Smart E2E Test Selection⏭️ Smart E2E selection skipped - PR targets a release branch (release/*) All E2E tests pre-selected. |
Contributor
|
✅ E2E Fixture Validation — Schema is up to date |
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Description
Migrate the Predict Buy and Sell preview screens from full-screen stack
navigation into BottomSheet wrappers, gated behind the
predictBottomSheetLaunchDarkly feature flag. When the flag is OFF,the existing full-screen navigation flow is preserved with zero
behavioral changes.
What changed
New:
PredictPreviewSheetwrapper componentchildren.
BottomSheet,BottomSheetHeader, andBottomSheetHeaderVariantfrom@metamask/design-system-react-native(not the deprecated component-library version).
renderHeaderprop for custom header content (sell sheetuses it for rich cashout info); falls back to default
icon+title+subtitle layout (buy sheet).
isFullscreenprop controls sheet height: content-fitted for both buyand sell sheets (auto-sizes to content).
testIDprops(
preview-sheet-title,preview-sheet-subtitle) for testID-basedassertions.
New:
PredictPreviewSheetContext(central sheet state manager)open/close lifecycle.
openBuySheet()/openSellSheet()methods consumed by allcard components.
sheet (new flow).
SellSheetHeaderextracted as a standalone component within thecontext for the rich header.
usePredictPreviewSheet()returns navigation-basedrouting when used outside the provider (e.g. home carousel, trending
feed) instead of throwing, with memoized references to prevent
unnecessary re-renders.
consecutive sheet opens.
New:
predictBottomSheetfeature flagfeature-flag-registry.tsas a remote, version-gatedflag (default: disabled).
selectPredictBottomSheetEnabledFlaginselectors/featureFlags/index.ts.New:
PredictQuickAmountscomponent (with haptic feedback)bottom content area of the buy sheet.
expo-haptics(impactAsyncwithImpactFeedbackStyle.Light)for tactile feedback on button press.
impactAsyncis properly awaitedwith error swallowing for unsupported devices.
"20"not"20.00") for easier editing.Boxcomponent instead of rawView.coverage.
New:
usePredictCashOuthook (shared cashout logic)position.outcomeId, callopenSellSheet, and handle errors withLogger.error+ toast notification.marketandcallerNameparameters;callerNameis includedin error metadata for production diagnostics.
PredictPicksandPredictPositionDetail,eliminating ~40 lines of identical code from each.
hooks/index.ts.openSellSheetparams,error/toast on missing outcome, and
callerNamein metadata.Navigation migration (10 components updated)
All components that previously called
navigateToBuyPreview()/navigate(Routes.PREDICT.MODALS.SELL_PREVIEW)now callopenBuySheet()/
openSellSheet()fromusePredictPreviewSheet:PredictMarketDetails- outcome buy actionsPredictMarketSingle- Yes/No buy buttonsPredictMarketMultiple- outcome buy buttonsPredictMarketOutcome- price buttonsPredictSportCardFooter- feed card buy actionsFeaturedCarouselCard- carousel card outcome buy buttonsFeaturedCarouselSportCard- sport carousel card buy buttonsPredictPicks- cashout from picks list via sharedusePredictCashOuthookPredictPositionDetail- cashout from position detail via sharedusePredictCashOuthookusePredictNavigationfrom all thesecomponents.
Routes (
routes/index.tsx)PredictPreviewSheetProvidersosheets are available throughout the Predict navigation tree.
Buy sheet (
PredictBuyWithAnyToken)mode: 'sheet' | 'screen') fortype-safe sheet vs navigation mode, replacing
Partial<ContentProps>with
ascasts.PredictPreviewSheet),PredictQuickAmountsin bottom content,PredictPayWithRowin compactvariant="row"mode with balance display, keypad moved below bottomcontent with
hideHeader, border hidden, action button displays"Confirm".
Boxwrapper instead ofSafeAreaViewin sheet mode.PredictPayWithAnyTokenInforeceivesisInputFocused: falsein sheetmode to ensure mm_pay relay configuration runs immediately (prevents
underfunded deposit).
onReject+clearActiveOrderTransactionId(matchesbeforeRemovebehavior in oldflow).
approvalRequestis missing duringhandleConfirmin PAY_WITH_ANY_TOKEN state, attempts re-initializationvia
initPayWithAnyToken()(clearsbatchIdRef, rejects pendingtransactions, re-creates the approval) before returning
PLACE_ORDER_FAILED. This gives the user a chance to retry with a freshapproval rather than silently failing.
Buy sheet (
PredictBuyPreview- legacy non-pay-with-any-token)isSheetModepattern for BottomSheetcompatibility.
onClose().ScrollViewfromreact-native-gesture-handler(aliased asGHScrollView) in sheet mode to cooperate with the BottomSheet'sPanGestureHandler; standard React NativeScrollViewpreserved forthe full-screen path.
Sell sheet (
PredictSellPreview)isSheetModedrivesconditional layout: icon+title row hidden in sheet mode (rendered by
SellSheetHeaderin the sheet header instead), price/shares/PnL sectionrelocated to the bottom area.
tw.style('flex-col')instead ofStyleSheet.create()for the container.getCashoutInfoTexthelper intoutils/format.tsto DRY upthe localized cashout info string.
PredictController(batch transaction fix)gasFeeToken: MATIC_CONTRACTS.collateralto theinitPayWithAnyTokenbatch submission, matching other batch calls. Thisensures mm_pay configures the relay step to bridge funds when the Safe
has insufficient USDC balance.
PredictKeypadhideHeaderprop to suppress the quick-amount row + Done buttonwhen rendered inside the buy sheet (avoids duplication with
PredictQuickAmounts).PredictPayWithRowvariant="row"mode: compact single-row layout showing tokenicon, symbol, balance, and right chevron for the sheet.
availableBalanceprop for displaying balance in row mode.PredictBuyAmountSectionhideAvailableBalanceprop to suppress the "Available: $X.XX"text in sheet mode.
PredictBuyBottomContenthideBorderprop to remove the top border separator in sheetmode.
(nested
Textinstead of sibling flex items).PredictBuyActionButtonisSheetModeprop: when true, displays "Confirm" label instead of"{outcome} · {price}".
PredictFeeSummary"Total" on the same line.
HeadingMd/BoldtoBodyMd/Mediumto match Pay With row.Bug fixes
usePredictBuyConditions: Auto-revert effect that switched back toPredict balance now requires
totalPayForPredictBalance > 0, preventingimmediate revert when the amount is 0 (which always caused the payment
method selection to snap back in sheet mode).
usePredictBuyConditions:shouldWaitForPayFeesnow also requirescurrentValue > 0.usePredictBuyActions: WhenapprovalRequestis missing duringhandleConfirmin PAY_WITH_ANY_TOKEN state, attempts re-initialization(
initPayWithAnyToken()) before returning early with error, giving theuser a retry path rather than silently failing. Removed dead fallback
branch that was unreachable (transactionId derived from the same
approvalRequest).
usePredictBuyActions:batchIdRefcleared at the start ofdoInit()to prevent stale values between transaction attempts.usePredictBuyActions: Sheet unmount cleanup mirrorsbeforeRemovebehavior from the old flow.
usePredictBuyActions: DEPOSITING and SUCCESS state effects checkisSheetModeto callonClose()instead ofStackActions.pop().usePredictBuyError: RemovedisInputFocusedfrom theblockingPayAlertMessagegate. The flag was permanently suppressinginsufficient-funds errors in sheet mode because
isInputFocusedinitialized to
trueand never transitioned tofalsewhen usingquick-amount buttons. The
isPayFeesLoadingcheck already handlesstale-data concerns.
PredictPicks/PredictPositionDetail: Cashout logic (outcomelookup,
openSellSheet, error handling withLogger.error+ toast)extracted into shared
usePredictCashOuthook to eliminate duplication.PredictPayWithAnyTokenInfo:isInputFocusedoverridden tofalsein sheet mode so
updatePendingAmountandsetPayTokenrunimmediately, ensuring mm_pay relay step is configured before
confirmation.
PredictController: AddedgasFeeTokentoinitPayWithAnyTokenbatch to fix missing mm_pay relay step that caused "Insufficient USDC
balance in Safe" errors.
PredictPreviewSheetContext: StabilizedonDismisscallbacks withuseCallbackto prevent unstablecloseSheet/onClosereferences fromcausing repeated SUCCESS/DEPOSITING effect re-fires.
Code quality
usePredictCashOuthook to deduplicate identicalonCashOutlogic (~40 lines each) fromPredictPicksandPredictPositionDetail. Both components now use a single hook callinstead of duplicating the guarded action, outcome lookup,
openSellSheetcall, and error handling withLogger.error+ toast.The
callerNameparameter preserves per-component error metadata.Viewwith design-systemBoxinPredictQuickAmounts.usePredictBottomSheet.PredictBuyPreviewProps,PredictSellPreviewProps) replacePartial<ContentProps>withascasts, providing compile-time safety for sheet vs navigation mode.
BUY_PREVIEW_SHEET,SELL_PREVIEW_SHEET.usePredictPreviewSheetto preventunstable function references.
BuyComponentselection inPredictPreviewSheetContextwithuseMemoto prevent unnecessary unmount/remount cycles.index.tsbarrel export forPredictPreviewSheetcomponent(file organization standard).
cardStyleInterpolatoronBUY_PREVIEWandSELL_PREVIEWstack screens for the flag-OFF full-screen path.require()withjest.requireActual()in all test mockfactories.
navigationRef) inroutes/index.test.tsx— moved tobeforeEach.PredictMarketDetails.test.tsxtoverify exact call parameters with
expect.objectContaining().predict.order.buyi18n key and its stale testmock reference.
PredictPreviewSheetfrom deprecated component-libraryBottomSheetto@metamask/design-system-react-nativeBottomSheet/BottomSheetHeader(DS-first UI rule). UpdatedusePredictBottomSheethook to importBottomSheetReffrom DS.shouldNavigateBack={false}with DS equivalent (omitgoBackprop); changed headerstyleprop totwClassName.PredictBuyPreview: Uses gesture-handler-awareScrollViewfromreact-native-gesture-handlerin sheet mode to prevent scroll/dismissconflicts with the BottomSheet's
PanGestureHandleron Android.toBeNull()assertions withnot.toBeOnTheScreen()intest files per unit-test guidelines.
getByText,queryByText) withtestID-based assertions (
getByTestId+toHaveTextContent) inPredictPreviewSheet.test.tsxandPredictPreviewSheetContext.test.tsx.act()toawait act(async () => { ... })inroutes/index.test.tsxto flush async navigator state updates andeliminate act() warnings.
Localization
predict.oddskey ("Odds").predict.order.confirmkey ("Confirm") for the sheet actionbutton.
predict.order.buykey ("Buy {{outcome}}").Documentation
(
docs/predict/predictions-comprehensive-guide.md).(
docs/predict/tickets/buy-sell-bottomsheet-migration.md).Changelog
CHANGELOG entry: null
Related issues
Refs:
PRED-707
Manual testing steps
Screenshots/Recordings
Before
Full-screen buy/sell preview navigation (flag OFF) - no visual changes
to this flow.
After
newDemoPRedict.mov
Pre-merge author checklist
Docs and MetaMask Mobile
Coding
Standards.
if applicable
guidelines).
Not required for external contributors.
Pre-merge reviewer checklist
app, test code being changed).
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
Note
Medium Risk
Touches Predict trade entry points and preview/cash-out flows, plus
introduces a new feature-flagged UI layer; regressions could affect
order preview UX or sheet/navigation behavior, though the legacy route
path remains as fallback.
Overview
Adds a new feature-flagged bet slip flow for Predict by introducing
PredictPreviewSheet+PredictPreviewSheetProvider/usePredictPreviewSheet, allowingbuy/sell previews to open in BottomSheets (with navigation fallback when
the provider/flag isn’t available).
Updates multiple Predict entry points (feed cards, carousel cards,
market single/multiple/outcome components, and sport footer) to call
openBuySheetinstead of navigating to the buy preview, and refactorscash-out handling into a shared
usePredictCashOuthook that opens thesell sheet and handles missing-outcome errors via logging + toast.
Extends buy preview screens (
PredictBuyPreview,PredictBuyWithAnyToken) to support a newmode: 'sheet'renderingpath (layout tweaks, keypad/header behavior, confirm CTA), adds new test
IDs/feature-flag selectors (
predictBottomSheet), and adjusts providerorder result shape and assorted tests to match the new behavior.
Reviewed by Cursor Bugbot for commit
ca83caf. Bugbot is set up for automated
code reviews on this repo. Configure
here.
Co-authored-by: Caainã Jeronimo caainaje@gmail.com
Co-authored-by: Luis Taniça matallui@gmail.com 27617bd