Skip to content

feat(perps): consume perps controller analytics contract in MetaMask Mobile#33095

Merged
gambinish merged 29 commits into
mainfrom
MANUAL-000002-feat-consume-perps-controller-analy
Jul 16, 2026
Merged

feat(perps): consume perps controller analytics contract in MetaMask Mobile#33095
gambinish merged 29 commits into
mainfrom
MANUAL-000002-feat-consume-perps-controller-analy

Conversation

@abretonc7s

@abretonc7s abretonc7s commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Description

Adopts the controller-owned perps analytics contract and completes the assigned tickets.

Beyond the initial contract adoption (register the five controller MetaMetrics events, entry/discovery/UTM/hlFeeRate/metamaskFee trackingData on the order lifecycle, controller search events from the market list, removal of duplicate client-side PERPS_TRADE_TRANSACTION emissions so submitted/terminal come only from the controller), this now also wires:

  • UTM merged onto all client-emitted PERPS_SCREEN_VIEWED ()
  • search query / result-tapped / abandoned with full prop sets, 500ms debounce ()
  • sort_applied / filter_applied incl. the watchlist filter
  • place_order button for ALL users incl. the deposit-required path
  • abandon_order on genuine exit — back/swipe/hardware-back and tab-switch-away, suppressed on internal TP/SL / payment-selector nav and after placement
  • screen_type=add_margin/remove_margin on margin modal open
  • watchlisted on asset_detail only
  • PERPS_TRANSACTION_CONSIDERED + PERPS_TRADE_QUOTE_RECEIVED now emitted

No SDK bump or patch required on mobile — Metro uses the correct ESM build. The published 9.2.1 CommonJS build bug is core-side and fixed in perps-controller 9.2.2 (MetaMask/core#9471; upstream nktkas/hyperliquid#165).

Deferred

  • order_execution_latency_ms wiring lands with the 9.2.2 bump (new TrackingData.orderExecutionLatencyMs).

Changelog

CHANGELOG entry: null

Related issues

Refs: MetaMask/core#9311

Manual testing steps

Feature: perps controller analytics contract consumption

  Scenario: order and search analytics use controller contract
    Given the app is running with @metamask/perps-controller 9.2.1
    And MetaMetrics is enabled

    When the user opens Perps markets and searches for a symbol
    Then Perp Search Query / Result Tapped / Abandoned events can fire from the market list

    When the user places, closes, cancels, flips, or updates TP/SL
    Then trackingData includes entryPoint/discovery fields and fee fields where available
    And the client does not emit a second Perp Trade Transaction for the same controller event

N/A for visual UI walkthrough — analytics-only change; validated via recipe state assertions + unit tests.

Screenshots/Recordings

Analytics-only change; no screenshots or video. Proof is recipe state assertions and unit tests in Validation Logs.

Validation Recipe

recipe.json (17 steps — state asserts for AC1–AC5 analytics contract consumption)
{
  "schema_version": 1,
  "title": "Consume perps controller analytics contract (MANUAL-000002)",
  "description": "Prove Mobile consumes perps-controller analytics contract: package version, MetaMetrics event registration, attribution wiring, search events, and no duplicate client trade-transaction emissions.",
  "validate": {
    "workflow": {
      "entry": "ac1-package-version",
      "nodes": {
        "ac1-package-version": {
          "action": "command",
          "intent": "AC1: confirm installed @metamask/perps-controller includes core#9311 contract",
          "proofTarget": "ac1",
          "cmd": "node -e \"const fs=require('fs'); const pkg=JSON.parse(fs.readFileSync('node_modules/@metamask/perps-controller/package.json','utf8')); const cl=fs.readFileSync('node_modules/@metamask/perps-controller/CHANGELOG.md','utf8'); if (!pkg.version.startsWith('9.')) { console.error('unexpected version', pkg.version); process.exit(1);} if (!cl.includes('#9311') && !cl.includes('PerpsAttributionContext')) { console.error('changelog missing contract markers'); process.exit(1);} console.log('ok', pkg.version);\"",
          "next": "ac1-assert-output"
        },
        "ac1-assert-output": {
          "action": "assert_output",
          "intent": "AC1: package version command printed ok",
          "proofTarget": "ac1",
          "source": "ac1-package-version",
          "stream": "stdout",
          "contains": "ok",
          "next": "ac2-search-query"
        },
        "ac2-search-query": {
          "action": "assert_file",
          "intent": "AC2: MetaMetrics registers Perp Search Query",
          "proofTarget": "ac2",
          "path": "app/core/Analytics/MetaMetrics.events.ts",
          "contains": "Perp Search Query",
          "next": "ac2-search-result"
        },
        "ac2-search-result": {
          "action": "assert_file",
          "intent": "AC2: MetaMetrics registers Perp Search Result Tapped",
          "proofTarget": "ac2",
          "path": "app/core/Analytics/MetaMetrics.events.ts",
          "contains": "Perp Search Result Tapped",
          "next": "ac2-search-abandoned"
        },
        "ac2-search-abandoned": {
          "action": "assert_file",
          "intent": "AC2: MetaMetrics registers Perp Search Abandoned",
          "proofTarget": "ac2",
          "path": "app/core/Analytics/MetaMetrics.events.ts",
          "contains": "Perp Search Abandoned",
          "next": "ac2-considered"
        },
        "ac2-considered": {
          "action": "assert_file",
          "intent": "AC2: MetaMetrics registers Perp Transaction Considered",
          "proofTarget": "ac2",
          "path": "app/core/Analytics/MetaMetrics.events.ts",
          "contains": "Perp Transaction Considered",
          "next": "ac2-quote"
        },
        "ac2-quote": {
          "action": "assert_file",
          "intent": "AC2: MetaMetrics registers Perp Trade Quote Received",
          "proofTarget": "ac2",
          "path": "app/core/Analytics/MetaMetrics.events.ts",
          "contains": "Perp Trade Quote Received",
          "next": "ac3-attribution-helper"
        },
        "ac3-attribution-helper": {
          "action": "assert_file",
          "intent": "AC3: attribution helper maps entryPoint",
          "proofTarget": "ac3",
          "path": "app/components/UI/Perps/utils/perpsAnalyticsAttribution.ts",
          "contains": "entryPoint",
          "next": "ac3-deeplink-utm"
        },
        "ac3-deeplink-utm": {
          "action": "assert_file",
          "intent": "AC3: deeplink handler applies UTM attribution context",
          "proofTarget": "ac3",
          "path": "app/core/DeeplinkManager/handlers/legacy/handlePerpsUrl.ts",
          "contains": "setPerpsUtmAttribution",
          "next": "ac3-order-entrypoint"
        },
        "ac3-order-entrypoint": {
          "action": "assert_file",
          "intent": "AC3: order ticket wires entry attribution helper into trackingData",
          "proofTarget": "ac3",
          "path": "app/components/UI/Perps/Views/PerpsOrderView/PerpsOrderView.tsx",
          "contains": "toPerpsEntryAttribution",
          "next": "ac3-order-hl-fee"
        },
        "ac3-order-hl-fee": {
          "action": "assert_file",
          "intent": "AC3: order ticket trackingData includes hlFeeRate",
          "proofTarget": "ac3",
          "path": "app/components/UI/Perps/Views/PerpsOrderView/PerpsOrderView.tsx",
          "contains": "hlFeeRate",
          "next": "ac3-close-entrypoint"
        },
        "ac3-close-entrypoint": {
          "action": "assert_file",
          "intent": "AC3: close position wires entry attribution helper into trackingData",
          "proofTarget": "ac3",
          "path": "app/components/UI/Perps/Views/PerpsClosePositionView/PerpsClosePositionView.tsx",
          "contains": "toPerpsEntryAttribution",
          "next": "ac4-search-event"
        },
        "ac4-search-event": {
          "action": "assert_file",
          "intent": "AC4: market list emits controller Search Query event",
          "proofTarget": "ac4",
          "path": "app/components/UI/Perps/Views/PerpsMarketListView/PerpsMarketListView.tsx",
          "contains": "PERPS_SEARCH_QUERY",
          "next": "ac5-no-client-trade-dup"
        },
        "ac5-no-client-trade-dup": {
          "action": "command",
          "intent": "AC5: usePerpsOrderExecution must not emit client-side PERPS_TRADE_TRANSACTION",
          "proofTarget": "ac5",
          "cmd": "node -e \"const fs=require('fs'); const src=fs.readFileSync('app/components/UI/Perps/hooks/usePerpsOrderExecution.ts','utf8'); if (/track\\\\(\\\\s*MetaMetricsEvents\\\\.PERPS_TRADE_TRANSACTION/.test(src)) { console.error('duplicate client PERPS_TRADE_TRANSACTION still present'); process.exit(1);} console.log('no-client-trade-dup-ok');\"",
          "next": "ac5-assert-output"
        },
        "ac5-assert-output": {
          "action": "assert_output",
          "intent": "AC5: confirm no client trade-transaction duplicate emission",
          "proofTarget": "ac5",
          "source": "ac5-no-client-trade-dup",
          "stream": "stdout",
          "contains": "no-client-trade-dup-ok",
          "next": "ac3-flip-metamask-fee"
        },
        "ac3-flip-metamask-fee": {
          "action": "assert_file",
          "intent": "AC3: flip sheet passes metamaskFee for controller flip analytics",
          "proofTarget": "ac3",
          "path": "app/components/UI/Perps/components/PerpsFlipPositionConfirmSheet/PerpsFlipPositionConfirmSheet.tsx",
          "contains": "metamaskFee",
          "next": "done"
        },
        "done": {
          "action": "end",
          "status": "pass",
          "intent": "All analytics contract consumption assertions passed"
        }
      }
    }
  }
}

Validation Logs

Command:

Full output (17/17 passed)
# MetaMask Recipe Run

Status: pass
Duration: 7.5s
Nodes: 17/17 passed

## Steps
- PASS ac1-package-version (command, 201ms): exitCode=0, stdout=ok 9.2.1

- PASS ac1-assert-output (assert_output, 93ms): source=ac1-package-version, stream=stdout, contains=ok
- PASS ac2-search-query (assert_file, 100ms): path=app/core/Analytics/MetaMetrics.events.ts
- PASS ac2-search-result (assert_file, 83ms): path=app/core/Analytics/MetaMetrics.events.ts
- PASS ac2-search-abandoned (assert_file, 69ms): path=app/core/Analytics/MetaMetrics.events.ts
- PASS ac2-considered (assert_file, 93ms): path=app/core/Analytics/MetaMetrics.events.ts
- PASS ac2-quote (assert_file, 116ms): path=app/core/Analytics/MetaMetrics.events.ts
- PASS ac3-attribution-helper (assert_file, 105ms): path=app/components/UI/Perps/utils/perpsAnalyticsAttribution.ts
- PASS ac3-deeplink-utm (assert_file, 143ms): path=app/core/DeeplinkManager/handlers/legacy/handlePerpsUrl.ts
- PASS ac3-order-entrypoint (assert_file, 310ms): path=app/components/UI/Perps/Views/PerpsOrderView/PerpsOrderView.tsx
- PASS ac3-order-hl-fee (assert_file, 219ms): path=app/components/UI/Perps/Views/PerpsOrderView/PerpsOrderView.tsx
- PASS ac3-close-entrypoint (assert_file, 170ms): path=app/components/UI/Perps/Views/PerpsClosePositionView/PerpsClosePositionView.tsx
- PASS ac4-search-event (assert_file, 286ms): path=app/components/UI/Perps/Views/PerpsMarketListView/PerpsMarketListView.tsx
- PASS ac5-no-client-trade-dup (command, 592ms): exitCode=0, stdout=no-client-trade-dup-ok

- PASS ac5-assert-output (assert_output, 366ms): source=ac5-no-client-trade-dup, stream=stdout, contains=no-client-trade-dup-ok
- PASS ac3-flip-metamask-fee (assert_file, 300ms): path=app/components/UI/Perps/components/PerpsFlipPositionConfirmSheet/PerpsFlipPositionConfirmSheet.tsx
- PASS done (end, 307ms)

---
mm-harness: dist/ is OLDER than src/ — this run uses the stale compiled code.
  Next: npm run build (or: trash dist/ to run straight from src via tsx)
Recipe libraries: metamask=/Users/deeeed/dev/metamask/metamask-harness/library (1 flows)
{
  "schemaVersion": 1,
  "command": "run",
  "adapter": "mobile",
  "status": "pass",
  "exitCode": 0,
  "recovered": [],
  "mutations": [],
  "reportPath": "/Users/deeeed/dev/metamask/metamask-mobile-1/temp/tasks/feat/manual-000002-0709-124928/artifacts/recipe-run/report.md",
  "result": {
    "status": "pass",
    "summaryPath": "/Users/deeeed/dev/metamask/metamask-mobile-1/temp/tasks/feat/manual-000002-0709-124928/artifacts/recipe-run/summary.json",
    "tracePath": "/Users/deeeed/dev/metamask/metamask-mobile-1/temp/tasks/feat/manual-000002-0709-124928/artifacts/recipe-run/trace.json",
    "artifactManifestPath": "/Users/deeeed/dev/metamask/metamask-mobile-1/temp/tasks/feat/manual-000002-0709-124928/artifacts/recipe-run/artifact-manifest.json",
    "recipePath": "/Users/deeeed/dev/metamask/metamask-mobile-1/temp/tasks/feat/manual-000002-0709-124928/artifacts/recipe-run/recipe.json"
  }
}

Pre-merge author checklist

Performance checks (if applicable)

  • I've tested on Android
    • N/A for this analytics-only change (validated via recipe/unit on iOS slot)
  • I've tested with a power user scenario
    • N/A — no UI/performance path change
  • I've instrumented key operations with Sentry traces for production performance metrics
    • N/A — analytics wiring only; existing Sentry paths unchanged

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.

Self-review fix note

Forwarded market-list source_section through market details into order trackingData discovery fields; added unit coverage for the handoff.


Note

Medium Risk
Large analytics surface across trade, search, and navigation with abandon/suppression logic; wrong events could skew product metrics but does not change order execution or funds handling.

Overview
Aligns MetaMask Mobile Perps with the controller-owned analytics contract: trade lifecycle events (PERPS_TRADE_TRANSACTION, partial fills) are no longer emitted from usePerpsOrderExecution—only trackingData is forwarded so the controller owns submitted/terminal events.

Attribution & trackingData — Adds toPerpsEntryAttribution / UTM helpers and threads entryPoint, discovery (source_section), hlFeeRate, and fee fields through order, close, cancel, flip, TP/SL, and margin flows. UTM is merged into client PERPS_SCREEN_VIEWED via usePerpsEventTracking.

New client events & funnels — Market list: debounced PERPS_SEARCH_QUERY, result taps, abandonment, sort/filter applied, and search screen views. Order screen: debounced PERPS_TRANSACTION_CONSIDERED, pay-token PERPS_TRADE_QUOTE_RECEIVED, place-order tap (all paths), and usePerpsAbandonOrderTracking for abandon_order on real exits (not child routes or after commit). Close flow gets the same abandon hook plus route buttonClicked / buttonLocation on screen views. Adjust margin screen views by mode; asset details adds watchlisted and forwards source_section into orders.

Supporting piecesderivePerpsTradeAction for considered vs executed action parity; payment-token selector dismissed tracking; navigation params extended for close entry CTAs and source_section.

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

abretonc7s and others added 3 commits July 9, 2026 14:00
Wire Mobile to the perps-controller analytics contract: register new
MetaMetrics events, propagate entry/discovery/UTM and fee fields into
trackingData, emit controller search events, and remove duplicate
client-side trade transaction emissions.

Co-authored-by: Cursor <cursoragent@cursor.com>
Document why deeplink UTM attribution failures are swallowed so
navigation continues when Engine/controller is unavailable.

Co-authored-by: Cursor <cursoragent@cursor.com>
…002)

Pass market-list source_section through market details into order route
params and toPerpsEntryAttribution so trade trackingData includes
discoverySource fields.

Co-authored-by: Cursor <cursoragent@cursor.com>
@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.

@abretonc7s

Copy link
Copy Markdown
Contributor Author

Automated dev run — MANUAL-000002

Metric Value
Run 4124510e
Duration ?
Model cursor/grok-4.5-fast-xhigh
Nudges 0
Worker report

Description

Consume the TAT-3463 @metamask/perps-controller analytics contract in MetaMask Mobile. Register the five new controller MetaMetrics events, wire entry/discovery/UTM/hlFeeRate/metamaskFee into order lifecycle trackingData, emit controller search events from market list, and remove duplicate client-side PERPS_TRADE_TRANSACTION emissions so submitted/terminal events come only from the controller pipeline. Package already at 9.2.1 (includes core#9311) — no bump required.

Changelog

CHANGELOG entry: null

Related issues

Fixes: https://consensyssoftware.atlassian.net/browse/TAT-3463
Refs: MetaMask/core#9311

Manual testing steps

Feature: perps controller analytics contract consumption

  Scenario: order and search analytics use controller contract
    Given the app is running with @metamask/perps-controller 9.2.1
    And MetaMetrics is enabled

    When the user opens Perps markets and searches for a symbol
    Then Perp Search Query / Result Tapped / Abandoned events can fire from the market list

    When the user places, closes, cancels, flips, or updates TP/SL
    Then trackingData includes entryPoint/discovery fields and fee fields where available
    And the client does not emit a second Perp Trade Transaction for the same controller event

N/A for visual UI walkthrough — analytics-only change; validated via recipe state assertions + unit tests.

Screenshots/Recordings

N/A — analytics/contract wiring only; no visual UI change. Proof is recipe state assertions and unit tests (see Validation Logs).

Before

N/A

After

N/A

Validation Recipe

recipe.json (17 steps — state asserts for AC1–AC5 analytics contract consumption)
{
  "schema_version": 1,
  "title": "Consume perps controller analytics contract (MANUAL-000002)",
  "description": "Prove Mobile consumes TAT-3463 perps-controller analytics contract: package version, MetaMetrics event registration, attribution wiring, search events, and no duplicate client trade-transaction emissions.",
  "validate": {
    "workflow": {
      "entry": "ac1-package-version",
      "nodes": {
        "ac1-package-version": {
          "action": "command",
          "intent": "AC1: confirm installed @metamask/perps-controller includes core#9311 contract",
          "proofTarget": "ac1",
          "cmd": "node -e \"const fs=require('fs'); const pkg=JSON.parse(fs.readFileSync('node_modules/@metamask/perps-controller/package.json','utf8')); const cl=fs.readFileSync('node_modules/@metamask/perps-controller/CHANGELOG.md','utf8'); if (!pkg.version.startsWith('9.')) { console.error('unexpected version', pkg.version); process.exit(1);} if (!cl.includes('#9311') && !cl.includes('PerpsAttributionContext')) { console.error('changelog missing TAT-3463 contract markers'); process.exit(1);} console.log('ok', pkg.version);\"",
          "next": "ac1-assert-output"
        },
        "ac1-assert-output": {
          "action": "assert_output",
          "intent": "AC1: package version command printed ok",
          "proofTarget": "ac1",
          "source": "ac1-package-version",
          "stream": "stdout",
          "contains": "ok",
          "next": "ac2-search-query"
        },
        "ac2-search-query": {
          "action": "assert_file",
          "intent": "AC2: MetaMetrics registers Perp Search Query",
          "proofTarget": "ac2",
          "path": "app/core/Analytics/MetaMetrics.events.ts",
          "contains": "Perp Search Query",
          "next": "ac2-search-result"
        },
        "ac2-search-result": {
          "action": "assert_file",
          "intent": "AC2: MetaMetrics registers Perp Search Result Tapped",
          "proofTarget": "ac2",
          "path": "app/core/Analytics/MetaMetrics.events.ts",
          "contains": "Perp Search Result Tapped",
          "next": "ac2-search-abandoned"
        },
        "ac2-search-abandoned": {
          "action": "assert_file",
          "intent": "AC2: MetaMetrics registers Perp Search Abandoned",
          "proofTarget": "ac2",
          "path": "app/core/Analytics/MetaMetrics.events.ts",
          "contains": "Perp Search Abandoned",
          "next": "ac2-considered"
        },
        "ac2-considered": {
          "action": "assert_file",
          "intent": "AC2: MetaMetrics registers Perp Transaction Considered",
          "proofTarget": "ac2",
          "path": "app/core/Analytics/MetaMetrics.events.ts",
          "contains": "Perp Transaction Considered",
          "next": "ac2-quote"
        },
        "ac2-quote": {
          "action": "assert_file",
          "intent": "AC2: MetaMetrics registers Perp Trade Quote Received",
          "proofTarget": "ac2",
          "path": "app/core/Analytics/MetaMetrics.events.ts",
          "contains": "Perp Trade Quote Received",
          "next": "ac3-attribution-helper"
        },
        "ac3-attribution-helper": {
          "action": "assert_file",
          "intent": "AC3: attribution helper maps entryPoint",
          "proofTarget": "ac3",
          "path": "app/components/UI/Perps/utils/perpsAnalyticsAttribution.ts",
          "contains": "entryPoint",
          "next": "ac3-deeplink-utm"
        },
        "ac3-deeplink-utm": {
          "action": "assert_file",
          "intent": "AC3: deeplink handler applies UTM attribution context",
          "proofTarget": "ac3",
          "path": "app/core/DeeplinkManager/handlers/legacy/handlePerpsUrl.ts",
          "contains": "setPerpsUtmAttribution",
          "next": "ac3-order-entrypoint"
        },
        "ac3-order-entrypoint": {
          "action": "assert_file",
          "intent": "AC3: order ticket wires entry attribution helper into trackingData",
          "proofTarget": "ac3",
          "path": "app/components/UI/Perps/Views/PerpsOrderView/PerpsOrderView.tsx",
          "contains": "toPerpsEntryAttribution",
          "next": "ac3-order-hl-fee"
        },
        "ac3-order-hl-fee": {
          "action": "assert_file",
          "intent": "AC3: order ticket trackingData includes hlFeeRate",
          "proofTarget": "ac3",
          "path": "app/components/UI/Perps/Views/PerpsOrderView/PerpsOrderView.tsx",
          "contains": "hlFeeRate",
          "next": "ac3-close-entrypoint"
        },
        "ac3-close-entrypoint": {
          "action": "assert_file",
          "intent": "AC3: close position wires entry attribution helper into trackingData",
          "proofTarget": "ac3",
          "path": "app/components/UI/Perps/Views/PerpsClosePositionView/PerpsClosePositionView.tsx",
          "contains": "toPerpsEntryAttribution",
          "next": "ac4-search-event"
        },
        "ac4-search-event": {
          "action": "assert_file",
          "intent": "AC4: market list emits controller Search Query event",
          "proofTarget": "ac4",
          "path": "app/components/UI/Perps/Views/PerpsMarketListView/PerpsMarketListView.tsx",
          "contains": "PERPS_SEARCH_QUERY",
          "next": "ac5-no-client-trade-dup"
        },
        "ac5-no-client-trade-dup": {
          "action": "command",
          "intent": "AC5: usePerpsOrderExecution must not emit client-side PERPS_TRADE_TRANSACTION",
          "proofTarget": "ac5",
          "cmd": "node -e \"const fs=require('fs'); const src=fs.readFileSync('app/components/UI/Perps/hooks/usePerpsOrderExecution.ts','utf8'); if (/track\\\\(\\\\s*MetaMetricsEvents\\\\.PERPS_TRADE_TRANSACTION/.test(src)) { console.error('duplicate client PERPS_TRADE_TRANSACTION still present'); process.exit(1);} console.log('no-client-trade-dup-ok');\"",
          "next": "ac5-assert-output"
        },
        "ac5-assert-output": {
          "action": "assert_output",
          "intent": "AC5: confirm no client trade-transaction duplicate emission",
          "proofTarget": "ac5",
          "source": "ac5-no-client-trade-dup",
          "stream": "stdout",
          "contains": "no-client-trade-dup-ok",
          "next": "ac3-flip-metamask-fee"
        },
        "ac3-flip-metamask-fee": {
          "action": "assert_file",
          "intent": "AC3: flip sheet passes metamaskFee for controller flip analytics",
          "proofTarget": "ac3",
          "path": "app/components/UI/Perps/components/PerpsFlipPositionConfirmSheet/PerpsFlipPositionConfirmSheet.tsx",
          "contains": "metamaskFee",
          "next": "done"
        },
        "done": {
          "action": "end",
          "status": "pass",
          "intent": "All analytics contract consumption assertions passed"
        }
      }
    }
  }
}

Validation Logs

Command:

"$RUNNER_CMD" run temp/tasks/feat/manual-000002-0709-124928/artifacts/recipe.json --adapter mobile --artifacts-dir temp/tasks/feat/manual-000002-0709-124928/artifacts/recipe-run --project-root /Users/deeeed/dev/metamask/metamask-mobile-1 --slot macwork-mmdev-1 --json
Full output (17/17 passed)
# MetaMask Recipe Run

Status: pass
Duration: 7.5s
Nodes: 17/17 passed

## Steps
- PASS ac1-package-version (command, 201ms): exitCode=0, stdout=ok 9.2.1

- PASS ac1-assert-output (assert_output, 93ms): source=ac1-package-version, stream=stdout, contains=ok
- PASS ac2-search-query (assert_file, 100ms): path=app/core/Analytics/MetaMetrics.events.ts
- PASS ac2-search-result (assert_file, 83ms): path=app/core/Analytics/MetaMetrics.events.ts
- PASS ac2-search-abandoned (assert_file, 69ms): path=app/core/Analytics/MetaMetrics.events.ts
- PASS ac2-considered (assert_file, 93ms): path=app/core/Analytics/MetaMetrics.events.ts
- PASS ac2-quote (assert_file, 116ms): path=app/core/Analytics/MetaMetrics.events.ts
- PASS ac3-attribution-helper (assert_file, 105ms): path=app/components/UI/Perps/utils/perpsAnalyticsAttribution.ts
- PASS ac3-deeplink-utm (assert_file, 143ms): path=app/core/DeeplinkManager/handlers/legacy/handlePerpsUrl.ts
- PASS ac3-order-entrypoint (assert_file, 310ms): path=app/components/UI/Perps/Views/PerpsOrderView/PerpsOrderView.tsx
- PASS ac3-order-hl-fee (assert_file, 219ms): path=app/components/UI/Perps/Views/PerpsOrderView/PerpsOrderView.tsx
- PASS ac3-close-entrypoint (assert_file, 170ms): path=app/components/UI/Perps/Views/PerpsClosePositionView/PerpsClosePositionView.tsx
- PASS ac4-search-event (assert_file, 286ms): path=app/components/UI/Perps/Views/PerpsMarketListView/PerpsMarketListView.tsx
- PASS ac5-no-client-trade-dup (command, 592ms): exitCode=0, stdout=no-client-trade-dup-ok

- PASS ac5-assert-output (assert_output, 366ms): source=ac5-no-client-trade-dup, stream=stdout, contains=no-client-trade-dup-ok
- PASS ac3-flip-metamask-fee (assert_file, 300ms): path=app/components/UI/Perps/components/PerpsFlipPositionConfirmSheet/PerpsFlipPositionConfirmSheet.tsx
- PASS done (end, 307ms)

---
mm-harness: dist/ is OLDER than src/ — this run uses the stale compiled code.
  Next: npm run build   (or: trash dist/ to run straight from src via tsx)
Recipe libraries: metamask=/Users/deeeed/dev/metamask/metamask-harness/library (1 flows)
{
  "schemaVersion": 1,
  "command": "run",
  "adapter": "mobile",
  "status": "pass",
  "exitCode": 0,
  "recovered": [],
  "mutations": [],
  "reportPath": "/Users/deeeed/dev/metamask/metamask-mobile-1/temp/tasks/feat/manual-000002-0709-124928/artifacts/recipe-run/report.md",
  "result": {
    "status": "pass",
    "summaryPath": "/Users/deeeed/dev/metamask/metamask-mobile-1/temp/tasks/feat/manual-000002-0709-124928/artifacts/recipe-run/summary.json",
    "tracePath": "/Users/deeeed/dev/metamask/metamask-mobile-1/temp/tasks/feat/manual-000002-0709-124928/artifacts/recipe-run/trace.json",
    "artifactManifestPath": "/Users/deeeed/dev/metamask/metamask-mobile-1/temp/tasks/feat/manual-000002-0709-124928/artifacts/recipe-run/artifact-manifest.json",
    "recipePath": "/Users/deeeed/dev/metamask/metamask-mobile-1/temp/tasks/feat/manual-000002-0709-124928/artifacts/recipe-run/recipe.json"
  }
}

Pre-merge author checklist

Performance checks (if applicable)

  • I've tested on Android
    • N/A for this analytics-only change (validated via recipe/unit on iOS slot)
  • I've tested with a power user scenario
    • N/A — no UI/performance path change
  • I've instrumented key operations with Sentry traces for production performance metrics
    • N/A — analytics wiring only; existing Sentry paths unchanged

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.

Self-review fix note

Forwarded market-list source_section through market details into order trackingData discovery fields; added unit coverage for the handoff.

@abretonc7s abretonc7s changed the title chore: prepare farmslot publication pkg-4124510e-mre3xkao feat(perps): consume perps controller analytics contract in MetaMask Mobile Jul 10, 2026
@abretonc7s abretonc7s marked this pull request as ready for review July 10, 2026 06:25
@abretonc7s abretonc7s requested review from a team as code owners July 10, 2026 06:25
…nsume-perps-controller-analy

Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	app/components/UI/Perps/hooks/usePerpsOrderExecution.test.ts
#	app/components/UI/Perps/hooks/usePerpsOrderExecution.ts
Comment thread app/components/UI/Perps/Views/PerpsMarketListView/PerpsMarketListView.tsx Outdated
Comment thread app/components/UI/Perps/Views/PerpsMarketListView/PerpsMarketListView.tsx Outdated
Cancel flows now include totalFee and marketPrice so TrackingData matches the controller contract after merging main.

Co-authored-by: Cursor <cursoragent@cursor.com>
@abretonc7s abretonc7s changed the title feat(perps): consume perps controller analytics contract in MetaMask Mobile feat(perps): consume perps controller analytics contract in MetaMask Mobile [NOT-READY] Jul 10, 2026

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

Requesting changes for Two search correctness defects: suggested watchlist results produce inaccurate tap metrics, and unresolved result counts are recorded as zero during abandonment.

} else if (trimmedQuery) {
source_section = PERPS_EVENT_VALUE.SOURCE_SECTION.ACTIVE_SEARCH;
const resultRank =
filteredMarkets.findIndex((m) => m.symbol === market.symbol) + 1;

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.

Please derive resultRank and results_count from the same collection rendered to the user.

In watchlist mode, the UI combines matching watchlist markets with matching suggestions, while filteredMarkets excludes suggestions.

Tapping a suggested result therefore omits result_rank and reports an incorrect result count. A shared visibleSearchResults collection would keep rendering and analytics consistent.

: 'browse';

lastEmittedSearchQueryRef.current = normalizedQuery;
lastEmittedSearchResultsCountRef.current = resultsSettled ? resultCount : 0;

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.

The stale count bugbot issue in the if statement below here is resolved, but resetting this value to 0 records "no results" when results are still loading. Please store undefined when resultsSettled=false and omit results_count from PERPS_SEARCH_ABANDONED, matching PERPS_SEARCH_QUERY.

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

Requesting changes for two search correctness defects: suggested watchlist results produce inaccurate tap metrics, and search exits during loading incorrectly record results_count: 0.

geositta
geositta previously approved these changes Jul 14, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

  • Selected E2E tags: SmokePerps, SmokeWalletPlatform, SmokeConfirmations
  • Selected Performance tags: @PerformancePreps
  • Risk Level: medium
  • AI Confidence: 88%
click to see 🤖 AI reasoning details

E2E Test Selection:
This PR is a comprehensive analytics enhancement for the Perps (perpetuals) trading feature with some functional changes:

SmokePerps (primary): The PR touches nearly every Perps view and hook:

  • PerpsOrderView: New abandon order tracking, transaction-considered events, trade quote received events, deposit confirm error handling, source_section param forwarding, hasPlacedOrderRef for commit tracking
  • PerpsClosePositionView: New abandon tracking, buttonClicked/buttonLocation route params, hasConfirmedCloseRef
  • PerpsMarketListView: Major search analytics refactor (PERPS_SEARCH_QUERY, PERPS_SEARCH_RESULT_TAPPED, PERPS_SEARCH_ABANDONED), new sort/filter tracking, visibleSearchResults memoization
  • PerpsMarketDetailsView: watchlisted property added to screen view, source_section forwarding to order screen
  • PerpsFlipPositionConfirmSheet: Additional tracking data (metamaskFee, metamaskFeeRate, protocolFeeRate, entryAttribution)
  • PerpsAdjustMarginView: New PERPS_SCREEN_VIEWED event added
  • New hooks: usePerpsAbandonOrderTracking (navigation depth tracking for abandon detection)
  • New utils: deriveTradeAction, perpsAnalyticsAttribution, perpsPaymentTokenSelection
  • handlePerpsUrl deeplink: UTM attribution parsing and storage

SmokeWalletPlatform: Per tag description, Perps is a section inside the Trending tab. Changes to PerpsMarketListView, PerpsMarketDetailsView, and other Perps views that are embedded in Trending affect this tag.

SmokeConfirmations: Changes to pay-with-modal.tsx and usePayWithCryptoSection.ts (both in the confirmations directory) add markPerpsPaymentTokenSelection() calls. These files are part of the confirmation pay-with flow used during Perps deposit-and-order transactions. The usePayWithPerpsSection hook also changed. These are functional changes to the confirmation flow, not just analytics.

Performance tests: The @PerformancePreps tag covers perps market loading, position management, add funds flow, and order execution. The PerpsOrderView and PerpsMarketListView changes include new useEffect hooks with debounced timers and memoized computations that could affect render performance. However, the changes are primarily analytics instrumentation (tracking events, refs, debounced timers) rather than core rendering path changes. The risk of performance regression is low but non-zero given the added complexity in PerpsOrderView and PerpsMarketListView.

Performance Test Selection:
The PerpsOrderView and PerpsMarketListView received significant additions: new useEffect hooks with debounced timers (1s for transaction-considered, 500ms for search query), new memoized computations (visibleSearchResults, visibleWatchlistMarkets, visibleSuggestedMarkets), and new navigation event listeners (usePerpsAbandonOrderTracking adds beforeRemove and blur listeners). These additions could affect render performance and responsiveness in the Perps market loading and order execution flows covered by @PerformancePreps.

View GitHub Actions results

@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown
Contributor

⚡ Performance Test Results

ℹ️ Performance test results are currently non-blocking and will not block this PR.

All tests passed · 2 tests · 1 device

📱 Devices tested (1)

Android: Google Pixel 8 Pro (v14.0)

✅ Passed Tests (2)
Test Platform Device Duration Team Recording
Perps add funds Android Google Pixel 8 Pro (v14.0) 7.79s @mm-perps-engineering-team 📹 Watch
Perps open position and close it Android Google Pixel 8 Pro (v14.0) 22.04s @mm-perps-engineering-team 📹 Watch

Branch: MANUAL-000002-feat-consume-perps-controller-analy · Build: Normal · Commit: 501dc0f · View full run

@OGPoyraz OGPoyraz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Confirmation changes LGTM

@gambinish gambinish enabled auto-merge July 16, 2026 15:14
@gambinish gambinish added this pull request to the merge queue Jul 16, 2026
Merged via the queue into main with commit 92562db Jul 16, 2026
187 of 190 checks passed
@gambinish gambinish deleted the MANUAL-000002-feat-consume-perps-controller-analy branch July 16, 2026 15:47
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 16, 2026
@metamask-ci metamask-ci Bot added the release-8.4.0 Issue or pull request that will be included in release 8.4.0 label Jul 16, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-8.4.0 Issue or pull request that will be included in release 8.4.0 risk:medium AI analysis: medium risk size-XL team-money-movement issues related to Money Movement features

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants