Skip to content

feat: creates headless ramps hook#26728

Closed
georgeweiler wants to merge 30 commits into
mainfrom
headless-ramps-poc
Closed

feat: creates headless ramps hook#26728
georgeweiler wants to merge 30 commits into
mainfrom
headless-ramps-poc

Conversation

@georgeweiler

@georgeweiler georgeweiler commented Feb 28, 2026

Copy link
Copy Markdown
Contributor

Description

Introduces a headless ramps API so external teams can embed buy flows without the default token/quote selection UI.

What changed:

  • useRampsQuickBuy hook — Hook for external teams to initiate a headless ramps buy flow:

    • Pre-fetches payment methods for an optional assetId
    • Exposes openBuyModal({ assetId, paymentMethodId, amount }) to launch checkout
    • Subscribes to RampsController:orderStatusChanged and exposes the latest order status update via order
    • Provides goToBuyOrder(orderId) to navigate to the order details screen
  • HeadlessBuy view — Screen that runs the actual buy flow. Receives assetId, paymentMethodId, and amount as params; fetches a quote; routes to native (Transak) or aggregator checkout; and closes when the order reaches a terminal status (Completed, Failed, Cancelled, IdExpired).

  • createHeadlessBuyNavDetails — Navigation helper that routes into the ramps stack with the right params for HeadlessBuy.

  • Routes.RAMP.HEADLESS_BUY — Route added and wired into the ramps stack.

  • HeadlessBuyDemo — QA demo component that uses useRampsQuickBuy to exercise token selection, payment method selection, amount input, and the buy flow. Not registered in main app routes.

What stays untouched: Existing ramps flows (token selection, build quote, checkout) remain unchanged. Headless ramps is additive.

Dependencies: Builds on the order state refactor in #26596, which moved V2 orders into RampsController and enables subscription to orderStatusChanged.

Changelog

CHANGELOG entry: Allow any page to open the buy experience

Related issues

Fixes: #26733

Manual testing steps

Feature: my feature name

  Scenario: user [verb for user action]
    Given [describe expected initial app state]

    When user [verb for user action]
    Then [describe expected outcome]

Screenshots/Recordings

Before

After

Pre-merge author checklist

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.

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

@metamaskbot metamaskbot added the team-money-movement issues related to Money Movement features label Feb 28, 2026
github-merge-queue Bot pushed a commit that referenced this pull request Mar 2, 2026
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

Moves V2 ramp order state management from Redux (`FiatOrder`) into the
`RampsController`, eliminating the conversion layer where clean
`RampsOrder` API data was mangled into a legacy shape and immediately
cast back.

This PR will allow for future PRs such as a headless ramps experience
where callers can subscribe to order updates:
#26728

or implementing websocket connections for real-time order status
updates:
MetaMask/core#8075



Please reference loom videos below for a demo of this PR.

**What changed:**

- **Order creation** (Checkout + Transak native): `addOrder(rampsOrder)`
stores directly in controller state. No more `createInitialFiatOrder()`,
`rampsOrderToFiatOrder()`, or `dispatch(addFiatOrder())`. Transak
deposit orders merge `paymentDetails` from the `TransakDepositOrder`
onto the `RampsOrder` before storing, preserving inline bank transfer
details.

- **Order detail screen**: `OrderDetails` + `OrderContent` read
`RampsOrder` from controller state via `useRampsOrders` hook. No Redux,
no `processFiatOrder`, no `useInterval` polling. Pull-to-refresh calls
`refreshOrder()`. Controller polling keeps data fresh automatically.

- **Bank details screen**: `V2BankDetails` reads order lifecycle from
controller state and fetches deposit-specific data (payment details)
from `TransakService`. Cancel and confirm use `providerOrderId`
directly.

- **Orders list**: `OrdersList` merges legacy `FiatOrder[]` from Redux
with V2 `RampsOrder[]` from controller state via `DisplayOrder`
projection. Legacy orders route to the aggregator detail screen; V2
orders route to the new controller-native detail screen.

- **Side effects**: `ramps-controller-init.ts` subscribes to
`RampsController:orderStatusChanged` for notification and analytics
handlers, and starts controller order polling on init. Follows the same
pattern as `TransactionController:transactionConfirmed` handlers.

- **Messenger**: `RampsControllerInitMessenger` delegates the
`orderStatusChanged` event so init-level subscriptions work.

**What stays untouched:** Legacy `AGGREGATOR`, `DEPOSIT`, and older
orders remain in Redux. Their processors, detail screens, types, and
polling are unchanged.

<!--
Write a short description of the changes included in this pull request,
also include relevant motivation and context. Have in mind the following
questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->

## **Changelog**

<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`

If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`

(This helps the Release Engineer do their job more quickly and
accurately)
-->

CHANGELOG entry: null

## **Related issues**

Core PR that added orders MetaMask/core#8045

Fixes:

## **Manual testing steps**

```gherkin
Feature: my feature name

  Scenario: user [verb for user action]
    Given [describe expected initial app state]

    When user [verb for user action]
    Then [describe expected outcome]
```

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**


![Image](https://github.com/user-attachments/assets/bb026e42-5cbe-4542-8169-e1375cd31a39)

Loom video of the controller state being polled, created and updated for
Aggregator order (Moonpay)
https://www.loom.com/share/70658e66e55c444cadc02aad407c3da2

Loom video of the controller state being polled, created and updated for
Native order (Transak)
https://www.loom.com/share/2c6d5941cbfa48c4be7cc36000e40e89

<!-- [screenshots/recordings] -->

## **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**

- [ ] 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.



<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Updates unified ramp checkout/order list navigation and switches V2
order creation/storage to controller state, which could impact order
visibility and detail routing for buy/deposit flows. Risk is mitigated
by updated unit tests, but regressions could affect transaction history
UX.
> 
> **Overview**
> **V2 ramp orders are now stored and read from `RampsController`
instead of being converted into legacy `FiatOrder` Redux state.**
`Checkout` uses `useRampsOrders` (`getOrderFromCallback`, `addOrder`)
and always `reset`s into `Routes.RAMP.RAMPS_ORDER_DETAILS` with
`showCloseButton`, even when order IDs are `null`.
> 
> **Orders list UI and navigation were reworked to merge legacy Redux
orders with controller V2 orders.** `OrdersList` now projects both into
a unified `DisplayOrder` list (`mergeDisplayOrders`), renders new list
rows, and routes presses by source/provider (legacy aggregator ->
`OrderDetails`, V2 -> `RampsOrderDetails`, deposit -> new
`DepositOrderDetails`, created deposit -> deposit flow).
> 
> Adds `Routes.DEPOSIT.ORDER_DETAILS` to the main navigator and updates
provider selection to consider completed controller orders when
computing previously-used providers. Tests/snapshots were updated
accordingly, and V2 toast wiring now passes `status` (typed as
`RampsOrderStatus`) instead of legacy `state`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
f7c7f73. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Base automatically changed from orders-refactor to main March 2, 2026 17:36
@github-actions github-actions Bot added size-XL and removed size-L labels Mar 2, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

⏭️ Smart E2E selection skipped - draft PR

All E2E tests pre-selected.

View GitHub Actions results

@github-actions

Copy link
Copy Markdown
Contributor

E2E Fixture Validation — Schema is up to date
17 value mismatches detected (expected — fixture represents an existing user).
View details

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
30.1% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@github-actions github-actions Bot locked and limited conversation to collaborators Mar 24, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

size-XL team-money-movement issues related to Money Movement features

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Headless On-Ramp Checkout API in mobile

2 participants