Skip to content

fix(ramps): TRAM-3534 UB2 V2 orderType + callback purchase analytics#29629

Open
amitabh94 wants to merge 3 commits intomainfrom
fix/tram-3534-analytics-only
Open

fix(ramps): TRAM-3534 UB2 V2 orderType + callback purchase analytics#29629
amitabh94 wants to merge 3 commits intomainfrom
fix/tram-3534-analytics-only

Conversation

@amitabh94
Copy link
Copy Markdown
Contributor

@amitabh94 amitabh94 commented May 1, 2026

Description

UB2 unified buy (V2) orders sometimes carried orderType as buy (lowercase) from the ramps controller precreated stub or API, while purchase analytics compared strictly to BUY. Buy completions could then emit off-ramp MetaMetrics events (OFFRAMP_PURCHASE_COMPLETED, etc.) even though the flow was on-ramp.

This PR:

  • Introduces app/util/ramps/normalizeRampsOrderTypeForFiatOrder.ts and uses isRampsOrderTypeBuy / isRampsOrderTypeSell in the ramps controller V2 analytics payload builder and in getRampsV2AnalyticsPayload.
  • Normalizes orderType when mapping a RampsOrder to FiatOrder in unifiedOrderProcessor.
  • After a WebView callback resolves in OrderDetails, calls existing handleOrderStatusChangedForMetrics when the order status changes (reuses the same ONRAMP_PURCHASE_COMPLETED / failed / cancelled pipeline as controller polling transitions).
  • Corrects OFFRAMP_PURCHASE_FAILED in Aggregator analytics types to OffRampPurchaseFailed.

Changelog

This change is not end-user visible in the product UI; it corrects analytics / instrumentation only.

CHANGELOG entry: null

Related issues

Fixes: https://consensyssoftware.atlassian.net/browse/TRAM-3534

Manual testing steps

Feature: UB2 on-ramp purchase analytics after checkout

  Scenario: Buy order completes after WebView checkout
    Given UB2 unified buy is available and the user starts a buy through the WebView checkout flow
    When the provider redirects back and the order resolves to a completed on-ramp order
    Then purchase completion analytics use the on-ramp completion event (not off-ramp), consistent with order type and status

  Scenario: Order type casing from API or stub
    Given A V2 order exists with orderType reported as buy (lowercase) or BUY
    When Terminal purchase analytics are emitted for that order
    Then Events follow on-ramp vs off-ramp routing based on normalized buy/sell semantics

Screenshots/Recordings

Not applicable (analytics / instrumentation only).

Before

N/A

After

N/A

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.

Automated tests run

yarn jest app/util/ramps/normalizeRampsOrderTypeForFiatOrder.test.ts \
  app/core/Engine/controllers/ramps-controller/event-handlers/analytics.test.ts \
  app/components/UI/Ramp/utils/getRampsV2AnalyticsPayload.test.ts \
  app/components/UI/Ramp/orderProcessor/unifiedOrderProcessor.test.ts \
  app/components/UI/Ramp/Views/OrderDetails/OrderDetails.test.tsx

Supersedes closed draft: #29628


Note

Medium Risk
Changes how V2 ramp orders are classified as BUY vs SELL and when terminal-status analytics events are emitted, which could affect downstream metrics and any logic keyed off FiatOrder.orderType. UI behavior is mostly unchanged, but the new callback-driven analytics trigger could alter event volumes/timing.

Overview
Fixes misclassified UB2 (RAMPS_V2) purchase analytics by normalizing orderType (e.g., handling lowercase/whitespace) and using shared isRampsOrderTypeBuy/isRampsOrderTypeSell helpers across the unified order processor and analytics payload builders.

Adds a guard to skip terminal-state analytics for non buy/sell order types (e.g., TRANSFER/DEPOSIT), and updates OrderDetails to emit purchase status-change analytics after callback-based order fetches when the fetched status differs from the prior stored status.

Corrects the AnalyticsEvents typing for OFFRAMP_PURCHASE_FAILED to use OffRampPurchaseFailed (instead of OfframpCanceled) and extends test coverage for the new normalization and callback analytics behavior.

Reviewed by Cursor Bugbot for commit f880d75. Bugbot is set up for automated code reviews on this repo. Configure here.

…trics

- Normalize Ramps orderType (buy/BUY) for FiatOrder and purchase event routing
- Use isRampsOrderTypeBuy/Sell in controller V2 analytics payload builder
- Fire handleOrderStatusChangedForMetrics after WebView callback when status changes
- Fix OFFRAMP_PURCHASE_FAILED Aggregator type (OffRampPurchaseFailed)

Refs: https://consensyssoftware.atlassian.net/browse/TRAM-3534
Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 1, 2026

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.

@codecov-commenter
Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.46154% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.83%. Comparing base (51b6bbd) to head (7c11eb8).
⚠️ Report is 34 commits behind head on main.

Files with missing lines Patch % Lines
...onents/UI/Ramp/Views/OrderDetails/OrderDetails.tsx 75.00% 0 Missing and 1 partial ⚠️
...onents/UI/Ramp/utils/getRampsV2AnalyticsPayload.ts 83.33% 0 Missing and 1 partial ⚠️
.../util/ramps/normalizeRampsOrderTypeForFiatOrder.ts 91.66% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #29629      +/-   ##
==========================================
- Coverage   82.15%   81.83%   -0.33%     
==========================================
  Files        5178     5227      +49     
  Lines      137450   138616    +1166     
  Branches    31079    31469     +390     
==========================================
+ Hits       112924   113434     +510     
- Misses      16875    17447     +572     
- Partials     7651     7735      +84     

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

@amitabh94 amitabh94 marked this pull request as ready for review May 1, 2026 21:09
@amitabh94 amitabh94 requested a review from a team as a code owner May 1, 2026 21:09
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 1, 2026

🔍 Smart E2E Test Selection

  • Selected E2E tags: SmokeMoney
  • Selected Performance tags: None (no tests recommended)
  • Risk Level: medium
  • AI Confidence: 88%
click to see 🤖 AI reasoning details

E2E Test Selection:
All 11 changed files are scoped to the Ramps (fiat on/off-ramp) feature:

  1. New utility (normalizeRampsOrderTypeForFiatOrder.ts): Normalizes V2 API order type strings (lowercase 'buy', 'SELL', 'TRANSFER', etc.) to canonical enum values. Used by analytics and order processor.

  2. Analytics event handler (ramps-controller/event-handlers/analytics.ts): Replaces direct === 'BUY' comparison with isRampsOrderTypeBuy/isRampsOrderTypeSell helpers, and adds early return for non-buy/sell order types (TRANSFER, DEPOSIT) to avoid spurious analytics events.

  3. getRampsV2AnalyticsPayload.ts: Same pattern — uses helpers, adds guard for terminal-state non-buy/sell orders returning [null, null].

  4. OrderDetails.tsx: Now calls handleOrderStatusChangedForMetrics when order status changes during the callback URL flow — previously this analytics call was missing.

  5. unifiedOrderProcessor.ts: Uses normalizeRampsOrderTypeForFiatOrder for proper type normalization instead of a direct cast.

  6. analytics.ts (types): Fixes OFFRAMP_PURCHASE_FAILED type from OfframpCanceled to OffRampPurchaseFailed.

These changes are entirely within the ramps/fiat on-off-ramp domain. The SmokeMoney tag covers ramps buy/sell flows, order details, and related analytics. No other feature areas (swaps, confirmations, accounts, networks) are affected. The changes are analytics/normalization fixes rather than core transaction logic, hence medium risk.

Performance Test Selection:
No performance-sensitive code was modified. Changes are limited to analytics event tracking, order type string normalization utilities, and a type fix. No UI rendering, list components, state management, or app initialization code was touched.

View GitHub Actions results

Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

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 1 potential issue.

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 f880d75. Configure here.

export function normalizeRampsOrderTypeForFiatOrder(
orderType: string | undefined,
): FiatOrder['orderType'] {
const upper = String(orderType ?? '').toUpperCase();
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.

Missing .trim() in normalize vs sibling predicates

Medium Severity

normalizeRampsOrderTypeForFiatOrder omits .trim() on line 12 while the sibling isRampsOrderTypeBuy and isRampsOrderTypeSell in the same file both .trim() before comparing. A whitespace-padded value like ' SELL ' is correctly identified as sell by the predicates (tested explicitly in the analytics test), but normalizeRampsOrderTypeForFiatOrder falls through to the raw passthrough on line 22, storing the un-normalized value in the FiatOrder. This causes a mismatch between how orders are classified for analytics vs how they're stored.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f880d75. Configure here.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented May 1, 2026

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants