feat(perps): [Analytics][Core] Consolidate perps-controller analytics contract and transaction pipeline - #9311
Merged
Conversation
Contributor
Author
Automated dev run — TAT-3463
Worker reportReport — TAT-3463 Consolidate perps-controller analytics contract + transaction pipelineSummaryAdded the consolidated Perps analytics contract (event names, property keys, value Changed files
Validation commands & results
Perps smoke recipe (step 10)
Risks
Client follow-up (separate tickets)
|
abretonc7s
marked this pull request as ready for review
June 30, 2026 09:12
abretonc7s
temporarily deployed
to
default-branch
June 30, 2026 09:12 — with
GitHub Actions
Inactive
…n pipeline Add the consolidated Perps analytics contract (five new PerpsAnalyticsEvent names, ticket-sourced PERPS_EVENT_PROPERTY keys and PERPS_EVENT_VALUE entries, PerpsAttributionContext API for UTM propagation) and the transaction-pipeline behavior in TradingService: status=submitted events before provider calls, metamask_fee on flip success, leverage on close, hl_fee_rate on trade/close (omitted when absent), bulk_action_id for batch close/cancel, and discovery attribution propagation. Additive only; no events renamed or removed. Ref: TAT-3463
…PR 9311 Co-authored-by: Cursor <cursoragent@cursor.com>
abretonc7s
force-pushed
the
TAT-3463-feat-consolidate-perps-analytics
branch
from
June 30, 2026 10:54
4fbaf2a to
3e429f6
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ 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 1d97624. Configure here.
Resolves the lint:misc:check CI failure by bringing the consolidated analytics/transaction-pipeline files into oxfmt compliance. Formatting only; no behavior or API changes.
abretonc7s
enabled auto-merge
June 30, 2026 16:39
gambinish
approved these changes
Jun 30, 2026
This was referenced Jun 30, 2026
pull Bot
pushed a commit
to Reality2byte/core
that referenced
this pull request
Jun 30, 2026
…ask#9322) ## Explanation Mobile ships Auto Close TP/SL RoE sign toggle analytics using interim local constants in `perpsEventsLocal.ts` (`tpsl_roe_sign_toggled`, `roe_sign`) because these keys were not yet in the published `@metamask/perps-controller` contract. After MetaMask#9311 consolidated the analytics contract in core, this small gap remained for the RoE sign toggle interaction. This PR adds the missing shared constants to `@metamask/perps-controller` so clients can import typed values instead of maintaining local mirrors: - `PERPS_EVENT_PROPERTY.ROE_SIGN` → `roe_sign` (the toggled sign: `'+'` | `'-'`) - `PERPS_EVENT_VALUE.INTERACTION_TYPE.TPSL_ROE_SIGN_TOGGLED` → `tpsl_roe_sign_toggled` No controller or runtime behavior changes — event emission stays client-side (`Perp UI Interaction`). Only the contract/constants surface is extended. ## References - https://consensyssoftware.atlassian.net/browse/TAT-3429 - Follow-up client wiring: https://consensyssoftware.atlassian.net/browse/TAT-3430 (Mobile + Extension replace `perpsEventsLocal.ts` after package release) - Builds on analytics consolidation: MetaMask#9311 ## Checklist - [x] I've updated the test suite for new or updated code as appropriate - [x] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [x] I've communicated my changes to consumers by [updating changelogs for packages I've changed](https://github.com/MetaMask/core/tree/main/docs/processes/updating-changelogs.md) - [x] I've introduced [breaking changes](https://github.com/MetaMask/core/tree/main/docs/processes/breaking-changes.md) in this PR and have prepared draft pull requests for clients and consumer packages to resolve them — N/A: additive constants only <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Additive analytics constants only; no runtime, auth, or trading behavior changes. > > **Overview** > Extends the shared Perps analytics contract in `@metamask/perps-controller` with constants for **Auto Close TP/SL RoE sign toggle** events, so mobile and extension can drop local mirrors (`perpsEventsLocal.ts`) and import typed keys from core. > > Adds **`PERPS_EVENT_PROPERTY.ROE_SIGN`** (`roe_sign`) for the toggled sign (`'+'` | `'-'`) and **`PERPS_EVENT_VALUE.INTERACTION_TYPE.TPSL_ROE_SIGN_TOGGLED`** (`tpsl_roe_sign_toggled`). Changelog and `eventNames` tests assert the new exports; **no controller or emission logic** changes in this package. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit f933d88. 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: Cursor <cursoragent@cursor.com>
This was referenced Jul 9, 2026
pull Bot
pushed a commit
to Reality2byte/metamask-mobile
that referenced
this pull request
Jul 16, 2026
…Mobile (MetaMask#33095) ## **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** ```gherkin 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** <details><summary>recipe.json (17 steps — state asserts for AC1–AC5 analytics contract consumption)</summary> ```json { "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('MetaMask#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" } } } } } ``` </details> ## **Validation Logs** Command: ```bash ``` <details><summary>Full output (17/17 passed)</summary> ``` # 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" } } ``` </details> ## **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. #### Performance checks (if applicable) - [x] I've tested on Android - N/A for this analytics-only change (validated via recipe/unit on iOS slot) - [x] I've tested with a power user scenario - N/A — no UI/performance path change - [x] 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. <!-- CURSOR_SUMMARY --> --- > [!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 pieces** — `derivePerpsTradeAction` for considered vs executed action parity; payment-token selector dismissed tracking; navigation params extended for close entry CTAs and `source_section`. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 6c19369. 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: Cursor <cursoragent@cursor.com> Co-authored-by: Nicholas Gambino <nicholas.gambino@consensys.net>
georgewrmarshall
pushed a commit
to MetaMask/metamask-mobile
that referenced
this pull request
Jul 16, 2026
…Mobile (#33095) ## **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** ```gherkin 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** <details><summary>recipe.json (17 steps — state asserts for AC1–AC5 analytics contract consumption)</summary> ```json { "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" } } } } } ``` </details> ## **Validation Logs** Command: ```bash ``` <details><summary>Full output (17/17 passed)</summary> ``` # 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" } } ``` </details> ## **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. #### Performance checks (if applicable) - [x] I've tested on Android - N/A for this analytics-only change (validated via recipe/unit on iOS slot) - [x] I've tested with a power user scenario - N/A — no UI/performance path change - [x] 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. <!-- CURSOR_SUMMARY --> --- > [!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 pieces** — `derivePerpsTradeAction` for considered vs executed action parity; payment-token selector dismissed tracking; navigation params extended for close entry CTAs and `source_section`. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 6c19369. 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: Cursor <cursoragent@cursor.com> Co-authored-by: Nicholas Gambino <nicholas.gambino@consensys.net>
pull Bot
pushed a commit
to firas9941/metamask-extension
that referenced
this pull request
Jul 31, 2026
…Extension (MetaMask#44324) ## **Description** Adopts the controller-owned perps analytics contract (`@metamask/perps-controller`) and completes the assigned analytics tickets. Beyond the initial contract adoption (re-export controller event/property/value defs, attribution APIs incl. `mergeAttributionContext`, `trackingData`/`hlFeeRate` on place/close/cancel/flip, removal of duplicate client-side transaction emissions so the controller TradingService owns submitted+terminal analytics, LavaMoat policy updates), this now also wires: - UTM merged onto all client-emitted `PERPS_SCREEN_VIEWED` events, with `source=deeplink` for deep links - `watchlisted` on the asset_detail screen only - `screen_type=error` + `error_type` + `screen_name` on every displayed-error path - `button_clicked` (close/reduce_exposure) + `button_location` on position_close - `sort_applied` / `filter_applied` with `sort_field`/`sort_direction`/`filter_category` - `PERPS_TRANSACTION_CONSIDERED` (debounced, gated to genuine user size edits) + trading-screen defaults - `screen_type=add_margin`/`remove_margin` on margin modal open - no-swallowed-exception cleanup (captureException instead of empty catch / log-and-continue) ### Perps controller dependency The latest `main` sync supplies `@metamask/perps-controller@10.0.0`, which includes the upstream Hyperliquid packaging fix. The temporary 9.2.1 yarn patch was removed during the sync. ### Deferred / N/A - N/A in the extension: `PERPS_TRADE_QUOTE_RECEIVED` (no async pay-with-token quote — fee calculation is synchronous), payment-token-selector-dismissed (no such selector). ## **Changelog** CHANGELOG entry: null ## **Related issues** Refs: MetaMask/core#9311, MetaMask/core#9471 ## **Manual testing steps** 1. Build/start the Extension with the bumped `@metamask/perps-controller`. 2. Open Perps (home → market list → market detail → order entry) and confirm screens still load and trade/close/cancel/TP-SL flows still work. 3. With analytics enabled, confirm Segment receives controller-emitted Perp Trade / Close / Cancel / Risk events (including `status: submitted` then terminal) and that the UI no longer double-fires the same transaction event names for those flows. 4. Open a Perps deep link / URL with `utm_*` or `source=` query params and confirm subsequent order lifecycle events include attribution fields when present. ## **Screenshots/Recordings** Live Recipe Protocol v1 proof passed **177/177 nodes** in **143 seconds** at exact PR head `a2eb9dcaeae90f61edf22c4b8b9f9b353bf3fe8a` with `@metamask/perps-controller@10.0.0`. - Runtime: one production LavaMoat build; the real MetaMetrics client sent Segment batches to the harness collector. - Published proof stack: `@deeeed/metamask-harness@0.26.5` + `@farmslot/recipe-harness@0.10.4`. - Focused regression: 21 changed Perps/analytics suites, 762 tests, no console-baseline violations; locale validation clean. - Coverage: attributed screen views; search result and abandonment; filter/sort/watchlist; trade and close; limit-order cancel; TP/SL; add/remove margin screens; position flip; considered/abandoned order; unknown-market error screen. - Assertions: run-bracketed live payloads with event order/count/property checks, including submitted + terminal controller events and duplicate-emission guards. - Run integrity: no recovery and no product mutation; the artifact package retains the executed root, 11 digest-pinned child recipes, summary, trace, diagnostics, and manifest. - Diagnostics: eight non-blocking warning/error entries were retained in `diagnostics.json`; none invalidated UI postconditions or analytics assertions. Final artifact paths in the PR slot: - `temp/fixes/44324/perps-analytics-exact-head-a2eb9dcaea-0265/report.md` - `temp/fixes/44324/perps-analytics-exact-head-a2eb9dcaea-0265/summary.json` - `temp/fixes/44324/perps-analytics-exact-head-a2eb9dcaea-0265/trace.json` - `temp/fixes/44324/perps-analytics-exact-head-a2eb9dcaea-0265/provenance.json` ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/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-extension/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. ## **Validation Recipe** <details> <summary>Composable live Recipe Protocol v1 proof (177/177 nodes)</summary> The root composes independent flows so a failed acceptance criterion is isolated and rerunnable. The artifact package retains the exact child documents listed by digest below. ```json { "$schema": "https://farmslot.io/schemas/recipe-v1.schema.json", "title": "MetaMask Extension Perps analytics live proof", "description": "Compose independent live-app flows that prove Perps analytics from the built Extension and its real MetaMetrics client.", "workflow": { "entry": "ready", "nodes": { "ready": { "action": "call", "ref": "perps-analytics.runtime-ready", "intent": "Prepare the live Extension once", "next": "attribution" }, "attribution": { "action": "call", "ref": "perps-analytics.attributed-screen", "intent": "Prove attributed asset-details analytics", "next": "search-result" }, "search-result": { "action": "call", "ref": "perps-analytics.search-result", "intent": "Prove the successful search funnel", "next": "search-abandon" }, "search-abandon": { "action": "call", "ref": "perps-analytics.search-abandon", "intent": "Prove the abandoned search funnel", "next": "discovery-controls" }, "discovery-controls": { "action": "call", "ref": "perps-analytics.discovery-controls", "intent": "Prove market filter, sort, and watchlist analytics", "next": "trade-close" }, "trade-close": { "action": "call", "ref": "perps-analytics.trade-close", "intent": "Prove the committed trade and close lifecycles", "next": "cancel-order" }, "cancel-order": { "action": "call", "ref": "perps-analytics.cancel-order", "intent": "Prove the committed order-cancel lifecycle", "next": "risk-management" }, "risk-management": { "action": "call", "ref": "perps-analytics.risk-management", "intent": "Prove the committed TP/SL lifecycle", "next": "margin-flip" }, "margin-flip": { "action": "call", "ref": "perps-analytics.margin-flip", "intent": "Prove margin screens and the committed flip lifecycle", "next": "considered-abandon" }, "considered-abandon": { "action": "call", "ref": "perps-analytics.considered-abandon", "intent": "Prove considered and abandoned order analytics", "next": "error-screen" }, "error-screen": { "action": "call", "ref": "perps-analytics.error-screen", "intent": "Prove unknown-market error analytics", "next": "done" }, "done": { "action": "end", "status": "pass" } } } } ``` <details> <summary>Resolved child recipe digests</summary> - `perps-analytics.attributed-screen` — `sha256:366ab0fa91b93d528c0b1e3567f050f37504f955d90d900757b2519d505e37ee` - `perps-analytics.cancel-order` — `sha256:08e49f8b3753b79b8058f32a50f04ed0d747576f1292bbeea92168ff9b81afa0` - `perps-analytics.considered-abandon` — `sha256:f4cad429c6209a1c8842daba3a081169d30d451bc025ba05f7c9dfbf874e4307` - `perps-analytics.discovery-controls` — `sha256:63259a262ffff22a301231e748195d67c3a40168b24ae754ed32c87368bdbd91` - `perps-analytics.error-screen` — `sha256:fe098d8b928d1228d0b36cf5cef57ea0c60bae42e6e66710301ef328b1ff902a` - `perps-analytics.margin-flip` — `sha256:ffe7eb7fe44a0c4e41009876a37856bec72cab162b3f410bfdf9bfa7277d5fa5` - `perps-analytics.risk-management` — `sha256:a198670ce27037ab93ac3f3fcd2c806cc8e554ad67f3ebda8c1d960afde13792` - `perps-analytics.runtime-ready` — `sha256:9f562eae962e306c28ab39ef588a193bdc2424f73168ee7d6300b9aa960b89a8` - `perps-analytics.search-abandon` — `sha256:94bfaf573cd1f6bba01fa0627e21858b6ff14374664b390f97e80a0cc6f7fdd0` - `perps-analytics.search-result` — `sha256:1de24f3a300f91faf9ecf7a09536cc0637ee5285866cefa59c4208d810e98b29` - `perps-analytics.trade-close` — `sha256:46935ae8b573b4ccd2e83ca462eeb657eb01879d7c01ff4cb285490fffba5598` </details> Run command: ```sh npx --yes --package=@deeeed/metamask-harness@0.26.5 mm-harness run temp/fixes/44324/perps-analytics-live.recipe.json --adapter extension --target "$PWD" --cdp-port 6662 --library live=temp/fixes/44324/analytics-live-library --artifacts-dir temp/fixes/44324/perps-analytics-exact-head-a2eb9dcaea-0265 --json-stream ``` </details> <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Wide Perps UI and background analytics changes alter which events fire and which properties they carry; regressions would affect metrics rather than funds, but incorrect deduplication or status values could skew funnel reporting. > > **Overview** > Aligns Extension Perps analytics with **`@metamask/perps-controller`**: `shared/constants/perps-events` now re-exports the controller contract (with Extension aliases), and background **`trackPerpsEvent`** merges stored UTM via **`mergeAttributionContext`** / **`perpsSetAttributionContext`**. > > **Attribution end-to-end:** Perps deeplinks set **`source=deeplink`** and forward **`utm_*`** (including unsigned params on signed links via **`handlerSearchParams: 'original'`**). **`PerpsAttributionProvider`** on the wallet Perps tab and **`usePerpsAttribution`** replace **`buildPerpsVipTrackingData`**, attaching entry/discovery, **`hlFeeRate`**, and trade action on background **`trackingData`**. > > **Event ownership:** UI stops double-emitting trade/close/cancel/risk success paths the controller already reports; transport/`{ success: false }` gaps keep **`PerpsError`**, error **screen views**, and targeted client events (e.g. margin **`{ success: false }`**, batch close **position count** with **EXECUTED** status). Adds abandon-order tracking, geo-block screen views, close-modal CTA props, and **`input_method`** on size controls. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit a2eb9dc. 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: Cursor <cursoragent@cursor.com>
5 of 7 tasks
pull Bot
pushed a commit
to Reality2byte/core
that referenced
this pull request
Aug 3, 2026
…sk#9320) ## Summary - Removes internal Jira ticket IDs (`TAT-XXXX`) from perps-controller comments, JSDoc, and test names added in MetaMask#9311 - Keeps the descriptive comment text unchanged; no runtime or API behavior changes Follow-up to MetaMask#9311 — the cleanup commit that was left off the merged branch. ## Test plan - [x] `yarn eslint` on the 8 changed files — clean - [x] Comment-only diff; no logic changes Made with [Cursor](https://cursor.com) Co-authored-by: Cursor <cursoragent@cursor.com>
pull Bot
pushed a commit
to Reality2byte/metamask-extension
that referenced
this pull request
Aug 4, 2026
## **Description** Re-lands the analytics-contract consumption from MetaMask#44324 after its revert in MetaMask#45087, without the deeplink-attribution route changes. This keeps the controller-owned analytics contract, event ownership and deduplication, UI funnel/error/abandonment tracking, and attribution consumption. It does not change any deeplink route handler, add `handlerSearchParams: original`, or forward original/unsigned parameters. The signed-only attribution follow-up is proposed separately in MetaMask#45123 for focused Security review. ## **Changelog** CHANGELOG entry: null ## **Related issues** Refs: MetaMask#44324, MetaMask#45087, MetaMask/core#9311, MetaMask/core#9471 Follow-up: [TAT-3646 — restore signed Perps deeplink attribution](https://consensyssoftware.atlassian.net/browse/TAT-3646) ## **Manual testing steps** 1. Build and start the Extension with Perps enabled. 2. Open Perps and navigate through the market list, market detail, and order entry screens. 3. With analytics consent enabled, complete trade, close, cancel, TP/SL, and margin flows. 4. Confirm each submitted and terminal transaction event is emitted once, and confirm search, abandonment, filter, sort, watchlist, and displayed-error events include their expected properties. <!-- ## **Screenshots/Recordings** Fresh exact-head live analytics evidence will be attached after the production build and composable recipe run complete. This evidence validates the analytics contract only; it is not deeplink-security evidence. --> ## **Live recipe evidence** The reusable recipe is merged at [MetaMask/experimental-metamask-recipe-perps@c7cd115](https://github.com/MetaMask/experimental-metamask-recipe-perps/blob/c7cd11526ba85c2601463ede7f8cdf9697e8bde0/recipes/perps/analytics-lifecycle.extension.recipe.json). The scrubbed, checksummed evidence is published at [abretonc7s/mm-extension-farm-artifacts@461d55a](https://github.com/abretonc7s/mm-extension-farm-artifacts/tree/461d55a9e1406dfd57695bf5b6ff84d25ff991bb/fixes/45119/analytics-lifecycle). <details> <summary>201/201-node live HyperLiquid testnet proof</summary> ```sh RECIPE_LIBRARY_PATH="perps=/path/to/experimental-metamask-recipe-perps" \ mm-harness run perps.analytics-lifecycle \ --adapter extension \ market=BTC market_query=btc ``` - Product head: `d5e5a0184f01dd5fb099313060f99399327d9208` - Recipe source: merged commit `c7cd11526ba85c2601463ede7f8cdf9697e8bde0` - Runner: `@metamask/recipe-runner` source commit `fea899ce2716fd54cced5b58652025930d42fbf9` - Runtime: `@farmslot/recipe-harness` 0.11.0 - Result: 201 passed, 0 failed, 136,200 ms; no recovery or runtime mutation - Safety: all ten mutating setup/cleanup executions reported `requested: testnet`, `isTestnet: true`, and finished with zero matching orders and positions - Coverage: attributed screen views; search query, result, and abandonment; discovery controls; trade/open and close; single and batch cancellation; TP/SL; margin dialogs; position reversal; order consideration and abandonment; displayed error screens - Assertions use exact event totals plus required-property matches, so duplicate emissions fail rather than passing on presence alone - Review: [report](https://github.com/abretonc7s/mm-extension-farm-artifacts/blob/461d55a9e1406dfd57695bf5b6ff84d25ff991bb/fixes/45119/analytics-lifecycle/report.md), [summary](https://github.com/abretonc7s/mm-extension-farm-artifacts/blob/461d55a9e1406dfd57695bf5b6ff84d25ff991bb/fixes/45119/analytics-lifecycle/summary.json), [trace](https://github.com/abretonc7s/mm-extension-farm-artifacts/blob/461d55a9e1406dfd57695bf5b6ff84d25ff991bb/fixes/45119/analytics-lifecycle/trace.json), and [SHA-256 checksums](https://github.com/abretonc7s/mm-extension-farm-artifacts/blob/461d55a9e1406dfd57695bf5b6ff84d25ff991bb/fixes/45119/analytics-lifecycle/SHA256SUMS) The trace proves analytics consumption after routing inside the running Extension. It deliberately does not claim external signed-deeplink parsing, canonicalization, or security behavior. </details> ## **Validation** - 18 affected Perps suites / 730 tests passed with no console-baseline violations. - `yarn lint:changed:fix` passed with zero errors. - `yarn lint:tsc` passed. - Final diff audit found no files under `shared/lib/deep-links`, no `handlerSearchParams: original`, no `withDeeplinkAttribution`, and no original/unsigned handler parameter access. - The composed live recipe passed 201/201 nodes against this exact product head. ## **Pre-merge author checklist** - [x] I have followed MetaMask Contributor Docs and MetaMask Extension Coding Standards. - [x] I have completed the PR template to the best of my ability. - [x] I have included tests where applicable. - [x] I have documented code using JSDoc where applicable. - [x] I have applied the right labels. Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I have manually tested the PR. - [ ] I confirm that this PR addresses the applicable acceptance criteria and includes the necessary testing evidence. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches MetaMetrics event shapes and ownership across many Perps flows; mis-deduping or wrong status/attribution would skew analytics but does not change trading or auth logic. > > **Overview** > Re-lands Perps analytics alignment with `@metamask/perps-controller`: **`shared/constants/perps-events`** now imports the controller contract (with Extension aliases), new MetaMetrics funnel events are registered, and the unused **`tutorial`** locale key is removed. > > **Background:** `perpsSetAttributionContext` is exposed and `mergeAttributionContext` is wired through `createPerpsInfrastructure` so controller-emitted transaction events pick up stored UTM fields before MetaMetrics. Tests consolidate on **`test/mocks/metamask-perps-controller.js`**. > > **UI:** `PerpsAttributionProvider` / **`usePerpsAttribution`** replace `buildPerpsVipTrackingData` and attach **`trackingData`** (fees, VIP, `hlFeeRate`, flow attribution) on cancel, close, flip, and TP/SL. Duplicate client **transaction** analytics are removed where the controller owns submitted/terminal events; gaps remain for transport throws, margin `{ success: false }`, batch close counts, **`trackPerpsErrorScreenViewed`**, geo-block screen views, **`usePerpsAbandonOrderTracking`**, and order-size **`input_method`** plumbing. **`derivePerpsTradeAction`** keeps considered vs executed `action` consistent (including omitting reduce-only opposite-side orders). > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit b5e316c. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
n3ps
pushed a commit
to MetaMask/metamask-extension
that referenced
this pull request
Aug 5, 2026
## **Description** Re-lands the analytics-contract consumption from #44324 after its revert in #45087, without the deeplink-attribution route changes. This keeps the controller-owned analytics contract, event ownership and deduplication, UI funnel/error/abandonment tracking, and attribution consumption. It does not change any deeplink route handler, add `handlerSearchParams: original`, or forward original/unsigned parameters. The signed-only attribution follow-up is proposed separately in #45123 for focused Security review. ## **Changelog** CHANGELOG entry: null ## **Related issues** Refs: #44324, #45087, MetaMask/core#9311, MetaMask/core#9471 Follow-up: [TAT-3646 — restore signed Perps deeplink attribution](https://consensyssoftware.atlassian.net/browse/TAT-3646) ## **Manual testing steps** 1. Build and start the Extension with Perps enabled. 2. Open Perps and navigate through the market list, market detail, and order entry screens. 3. With analytics consent enabled, complete trade, close, cancel, TP/SL, and margin flows. 4. Confirm each submitted and terminal transaction event is emitted once, and confirm search, abandonment, filter, sort, watchlist, and displayed-error events include their expected properties. <!-- ## **Screenshots/Recordings** Fresh exact-head live analytics evidence will be attached after the production build and composable recipe run complete. This evidence validates the analytics contract only; it is not deeplink-security evidence. --> ## **Live recipe evidence** The reusable recipe is merged at [MetaMask/experimental-metamask-recipe-perps@c7cd115](https://github.com/MetaMask/experimental-metamask-recipe-perps/blob/c7cd11526ba85c2601463ede7f8cdf9697e8bde0/recipes/perps/analytics-lifecycle.extension.recipe.json). The scrubbed, checksummed evidence is published at [abretonc7s/mm-extension-farm-artifacts@461d55a](https://github.com/abretonc7s/mm-extension-farm-artifacts/tree/461d55a9e1406dfd57695bf5b6ff84d25ff991bb/fixes/45119/analytics-lifecycle). <details> <summary>201/201-node live HyperLiquid testnet proof</summary> ```sh RECIPE_LIBRARY_PATH="perps=/path/to/experimental-metamask-recipe-perps" \ mm-harness run perps.analytics-lifecycle \ --adapter extension \ market=BTC market_query=btc ``` - Product head: `d5e5a0184f01dd5fb099313060f99399327d9208` - Recipe source: merged commit `c7cd11526ba85c2601463ede7f8cdf9697e8bde0` - Runner: `@metamask/recipe-runner` source commit `fea899ce2716fd54cced5b58652025930d42fbf9` - Runtime: `@farmslot/recipe-harness` 0.11.0 - Result: 201 passed, 0 failed, 136,200 ms; no recovery or runtime mutation - Safety: all ten mutating setup/cleanup executions reported `requested: testnet`, `isTestnet: true`, and finished with zero matching orders and positions - Coverage: attributed screen views; search query, result, and abandonment; discovery controls; trade/open and close; single and batch cancellation; TP/SL; margin dialogs; position reversal; order consideration and abandonment; displayed error screens - Assertions use exact event totals plus required-property matches, so duplicate emissions fail rather than passing on presence alone - Review: [report](https://github.com/abretonc7s/mm-extension-farm-artifacts/blob/461d55a9e1406dfd57695bf5b6ff84d25ff991bb/fixes/45119/analytics-lifecycle/report.md), [summary](https://github.com/abretonc7s/mm-extension-farm-artifacts/blob/461d55a9e1406dfd57695bf5b6ff84d25ff991bb/fixes/45119/analytics-lifecycle/summary.json), [trace](https://github.com/abretonc7s/mm-extension-farm-artifacts/blob/461d55a9e1406dfd57695bf5b6ff84d25ff991bb/fixes/45119/analytics-lifecycle/trace.json), and [SHA-256 checksums](https://github.com/abretonc7s/mm-extension-farm-artifacts/blob/461d55a9e1406dfd57695bf5b6ff84d25ff991bb/fixes/45119/analytics-lifecycle/SHA256SUMS) The trace proves analytics consumption after routing inside the running Extension. It deliberately does not claim external signed-deeplink parsing, canonicalization, or security behavior. </details> ## **Validation** - 18 affected Perps suites / 730 tests passed with no console-baseline violations. - `yarn lint:changed:fix` passed with zero errors. - `yarn lint:tsc` passed. - Final diff audit found no files under `shared/lib/deep-links`, no `handlerSearchParams: original`, no `withDeeplinkAttribution`, and no original/unsigned handler parameter access. - The composed live recipe passed 201/201 nodes against this exact product head. ## **Pre-merge author checklist** - [x] I have followed MetaMask Contributor Docs and MetaMask Extension Coding Standards. - [x] I have completed the PR template to the best of my ability. - [x] I have included tests where applicable. - [x] I have documented code using JSDoc where applicable. - [x] I have applied the right labels. Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I have manually tested the PR. - [ ] I confirm that this PR addresses the applicable acceptance criteria and includes the necessary testing evidence. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches MetaMetrics event shapes and ownership across many Perps flows; mis-deduping or wrong status/attribution would skew analytics but does not change trading or auth logic. > > **Overview** > Re-lands Perps analytics alignment with `@metamask/perps-controller`: **`shared/constants/perps-events`** now imports the controller contract (with Extension aliases), new MetaMetrics funnel events are registered, and the unused **`tutorial`** locale key is removed. > > **Background:** `perpsSetAttributionContext` is exposed and `mergeAttributionContext` is wired through `createPerpsInfrastructure` so controller-emitted transaction events pick up stored UTM fields before MetaMetrics. Tests consolidate on **`test/mocks/metamask-perps-controller.js`**. > > **UI:** `PerpsAttributionProvider` / **`usePerpsAttribution`** replace `buildPerpsVipTrackingData` and attach **`trackingData`** (fees, VIP, `hlFeeRate`, flow attribution) on cancel, close, flip, and TP/SL. Duplicate client **transaction** analytics are removed where the controller owns submitted/terminal events; gaps remain for transport throws, margin `{ success: false }`, batch close counts, **`trackPerpsErrorScreenViewed`**, geo-block screen views, **`usePerpsAbandonOrderTracking`**, and order-size **`input_method`** plumbing. **`derivePerpsTradeAction`** keeps considered vs executed `action` consistent (including omitting reduce-only opposite-side orders). > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit b5e316c. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
Bigshmow
pushed a commit
to MetaMask/metamask-extension
that referenced
this pull request
Aug 5, 2026
…Extension (#44324) ## **Description** Adopts the controller-owned perps analytics contract (`@metamask/perps-controller`) and completes the assigned analytics tickets. Beyond the initial contract adoption (re-export controller event/property/value defs, attribution APIs incl. `mergeAttributionContext`, `trackingData`/`hlFeeRate` on place/close/cancel/flip, removal of duplicate client-side transaction emissions so the controller TradingService owns submitted+terminal analytics, LavaMoat policy updates), this now also wires: - UTM merged onto all client-emitted `PERPS_SCREEN_VIEWED` events, with `source=deeplink` for deep links - `watchlisted` on the asset_detail screen only - `screen_type=error` + `error_type` + `screen_name` on every displayed-error path - `button_clicked` (close/reduce_exposure) + `button_location` on position_close - `sort_applied` / `filter_applied` with `sort_field`/`sort_direction`/`filter_category` - `PERPS_TRANSACTION_CONSIDERED` (debounced, gated to genuine user size edits) + trading-screen defaults - `screen_type=add_margin`/`remove_margin` on margin modal open - no-swallowed-exception cleanup (captureException instead of empty catch / log-and-continue) ### Perps controller dependency The latest `main` sync supplies `@metamask/perps-controller@10.0.0`, which includes the upstream Hyperliquid packaging fix. The temporary 9.2.1 yarn patch was removed during the sync. ### Deferred / N/A - N/A in the extension: `PERPS_TRADE_QUOTE_RECEIVED` (no async pay-with-token quote — fee calculation is synchronous), payment-token-selector-dismissed (no such selector). ## **Changelog** CHANGELOG entry: null ## **Related issues** Refs: MetaMask/core#9311, MetaMask/core#9471 ## **Manual testing steps** 1. Build/start the Extension with the bumped `@metamask/perps-controller`. 2. Open Perps (home → market list → market detail → order entry) and confirm screens still load and trade/close/cancel/TP-SL flows still work. 3. With analytics enabled, confirm Segment receives controller-emitted Perp Trade / Close / Cancel / Risk events (including `status: submitted` then terminal) and that the UI no longer double-fires the same transaction event names for those flows. 4. Open a Perps deep link / URL with `utm_*` or `source=` query params and confirm subsequent order lifecycle events include attribution fields when present. ## **Screenshots/Recordings** Live Recipe Protocol v1 proof passed **177/177 nodes** in **143 seconds** at exact PR head `a2eb9dcaeae90f61edf22c4b8b9f9b353bf3fe8a` with `@metamask/perps-controller@10.0.0`. - Runtime: one production LavaMoat build; the real MetaMetrics client sent Segment batches to the harness collector. - Published proof stack: `@deeeed/metamask-harness@0.26.5` + `@farmslot/recipe-harness@0.10.4`. - Focused regression: 21 changed Perps/analytics suites, 762 tests, no console-baseline violations; locale validation clean. - Coverage: attributed screen views; search result and abandonment; filter/sort/watchlist; trade and close; limit-order cancel; TP/SL; add/remove margin screens; position flip; considered/abandoned order; unknown-market error screen. - Assertions: run-bracketed live payloads with event order/count/property checks, including submitted + terminal controller events and duplicate-emission guards. - Run integrity: no recovery and no product mutation; the artifact package retains the executed root, 11 digest-pinned child recipes, summary, trace, diagnostics, and manifest. - Diagnostics: eight non-blocking warning/error entries were retained in `diagnostics.json`; none invalidated UI postconditions or analytics assertions. Final artifact paths in the PR slot: - `temp/fixes/44324/perps-analytics-exact-head-a2eb9dcaea-0265/report.md` - `temp/fixes/44324/perps-analytics-exact-head-a2eb9dcaea-0265/summary.json` - `temp/fixes/44324/perps-analytics-exact-head-a2eb9dcaea-0265/trace.json` - `temp/fixes/44324/perps-analytics-exact-head-a2eb9dcaea-0265/provenance.json` ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/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-extension/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. ## **Validation Recipe** <details> <summary>Composable live Recipe Protocol v1 proof (177/177 nodes)</summary> The root composes independent flows so a failed acceptance criterion is isolated and rerunnable. The artifact package retains the exact child documents listed by digest below. ```json { "$schema": "https://farmslot.io/schemas/recipe-v1.schema.json", "title": "MetaMask Extension Perps analytics live proof", "description": "Compose independent live-app flows that prove Perps analytics from the built Extension and its real MetaMetrics client.", "workflow": { "entry": "ready", "nodes": { "ready": { "action": "call", "ref": "perps-analytics.runtime-ready", "intent": "Prepare the live Extension once", "next": "attribution" }, "attribution": { "action": "call", "ref": "perps-analytics.attributed-screen", "intent": "Prove attributed asset-details analytics", "next": "search-result" }, "search-result": { "action": "call", "ref": "perps-analytics.search-result", "intent": "Prove the successful search funnel", "next": "search-abandon" }, "search-abandon": { "action": "call", "ref": "perps-analytics.search-abandon", "intent": "Prove the abandoned search funnel", "next": "discovery-controls" }, "discovery-controls": { "action": "call", "ref": "perps-analytics.discovery-controls", "intent": "Prove market filter, sort, and watchlist analytics", "next": "trade-close" }, "trade-close": { "action": "call", "ref": "perps-analytics.trade-close", "intent": "Prove the committed trade and close lifecycles", "next": "cancel-order" }, "cancel-order": { "action": "call", "ref": "perps-analytics.cancel-order", "intent": "Prove the committed order-cancel lifecycle", "next": "risk-management" }, "risk-management": { "action": "call", "ref": "perps-analytics.risk-management", "intent": "Prove the committed TP/SL lifecycle", "next": "margin-flip" }, "margin-flip": { "action": "call", "ref": "perps-analytics.margin-flip", "intent": "Prove margin screens and the committed flip lifecycle", "next": "considered-abandon" }, "considered-abandon": { "action": "call", "ref": "perps-analytics.considered-abandon", "intent": "Prove considered and abandoned order analytics", "next": "error-screen" }, "error-screen": { "action": "call", "ref": "perps-analytics.error-screen", "intent": "Prove unknown-market error analytics", "next": "done" }, "done": { "action": "end", "status": "pass" } } } } ``` <details> <summary>Resolved child recipe digests</summary> - `perps-analytics.attributed-screen` — `sha256:366ab0fa91b93d528c0b1e3567f050f37504f955d90d900757b2519d505e37ee` - `perps-analytics.cancel-order` — `sha256:08e49f8b3753b79b8058f32a50f04ed0d747576f1292bbeea92168ff9b81afa0` - `perps-analytics.considered-abandon` — `sha256:f4cad429c6209a1c8842daba3a081169d30d451bc025ba05f7c9dfbf874e4307` - `perps-analytics.discovery-controls` — `sha256:63259a262ffff22a301231e748195d67c3a40168b24ae754ed32c87368bdbd91` - `perps-analytics.error-screen` — `sha256:fe098d8b928d1228d0b36cf5cef57ea0c60bae42e6e66710301ef328b1ff902a` - `perps-analytics.margin-flip` — `sha256:ffe7eb7fe44a0c4e41009876a37856bec72cab162b3f410bfdf9bfa7277d5fa5` - `perps-analytics.risk-management` — `sha256:a198670ce27037ab93ac3f3fcd2c806cc8e554ad67f3ebda8c1d960afde13792` - `perps-analytics.runtime-ready` — `sha256:9f562eae962e306c28ab39ef588a193bdc2424f73168ee7d6300b9aa960b89a8` - `perps-analytics.search-abandon` — `sha256:94bfaf573cd1f6bba01fa0627e21858b6ff14374664b390f97e80a0cc6f7fdd0` - `perps-analytics.search-result` — `sha256:1de24f3a300f91faf9ecf7a09536cc0637ee5285866cefa59c4208d810e98b29` - `perps-analytics.trade-close` — `sha256:46935ae8b573b4ccd2e83ca462eeb657eb01879d7c01ff4cb285490fffba5598` </details> Run command: ```sh npx --yes --package=@deeeed/metamask-harness@0.26.5 mm-harness run temp/fixes/44324/perps-analytics-live.recipe.json --adapter extension --target "$PWD" --cdp-port 6662 --library live=temp/fixes/44324/analytics-live-library --artifacts-dir temp/fixes/44324/perps-analytics-exact-head-a2eb9dcaea-0265 --json-stream ``` </details> <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Wide Perps UI and background analytics changes alter which events fire and which properties they carry; regressions would affect metrics rather than funds, but incorrect deduplication or status values could skew funnel reporting. > > **Overview** > Aligns Extension Perps analytics with **`@metamask/perps-controller`**: `shared/constants/perps-events` now re-exports the controller contract (with Extension aliases), and background **`trackPerpsEvent`** merges stored UTM via **`mergeAttributionContext`** / **`perpsSetAttributionContext`**. > > **Attribution end-to-end:** Perps deeplinks set **`source=deeplink`** and forward **`utm_*`** (including unsigned params on signed links via **`handlerSearchParams: 'original'`**). **`PerpsAttributionProvider`** on the wallet Perps tab and **`usePerpsAttribution`** replace **`buildPerpsVipTrackingData`**, attaching entry/discovery, **`hlFeeRate`**, and trade action on background **`trackingData`**. > > **Event ownership:** UI stops double-emitting trade/close/cancel/risk success paths the controller already reports; transport/`{ success: false }` gaps keep **`PerpsError`**, error **screen views**, and targeted client events (e.g. margin **`{ success: false }`**, batch close **position count** with **EXECUTED** status). Adds abandon-order tracking, geo-block screen views, close-modal CTA props, and **`input_method`** on size controls. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit a2eb9dc. 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: Cursor <cursoragent@cursor.com>
Bigshmow
pushed a commit
to MetaMask/metamask-extension
that referenced
this pull request
Aug 5, 2026
## **Description** Re-lands the analytics-contract consumption from #44324 after its revert in #45087, without the deeplink-attribution route changes. This keeps the controller-owned analytics contract, event ownership and deduplication, UI funnel/error/abandonment tracking, and attribution consumption. It does not change any deeplink route handler, add `handlerSearchParams: original`, or forward original/unsigned parameters. The signed-only attribution follow-up is proposed separately in #45123 for focused Security review. ## **Changelog** CHANGELOG entry: null ## **Related issues** Refs: #44324, #45087, MetaMask/core#9311, MetaMask/core#9471 Follow-up: [TAT-3646 — restore signed Perps deeplink attribution](https://consensyssoftware.atlassian.net/browse/TAT-3646) ## **Manual testing steps** 1. Build and start the Extension with Perps enabled. 2. Open Perps and navigate through the market list, market detail, and order entry screens. 3. With analytics consent enabled, complete trade, close, cancel, TP/SL, and margin flows. 4. Confirm each submitted and terminal transaction event is emitted once, and confirm search, abandonment, filter, sort, watchlist, and displayed-error events include their expected properties. <!-- ## **Screenshots/Recordings** Fresh exact-head live analytics evidence will be attached after the production build and composable recipe run complete. This evidence validates the analytics contract only; it is not deeplink-security evidence. --> ## **Live recipe evidence** The reusable recipe is merged at [MetaMask/experimental-metamask-recipe-perps@c7cd115](https://github.com/MetaMask/experimental-metamask-recipe-perps/blob/c7cd11526ba85c2601463ede7f8cdf9697e8bde0/recipes/perps/analytics-lifecycle.extension.recipe.json). The scrubbed, checksummed evidence is published at [abretonc7s/mm-extension-farm-artifacts@461d55a](https://github.com/abretonc7s/mm-extension-farm-artifacts/tree/461d55a9e1406dfd57695bf5b6ff84d25ff991bb/fixes/45119/analytics-lifecycle). <details> <summary>201/201-node live HyperLiquid testnet proof</summary> ```sh RECIPE_LIBRARY_PATH="perps=/path/to/experimental-metamask-recipe-perps" \ mm-harness run perps.analytics-lifecycle \ --adapter extension \ market=BTC market_query=btc ``` - Product head: `d5e5a0184f01dd5fb099313060f99399327d9208` - Recipe source: merged commit `c7cd11526ba85c2601463ede7f8cdf9697e8bde0` - Runner: `@metamask/recipe-runner` source commit `fea899ce2716fd54cced5b58652025930d42fbf9` - Runtime: `@farmslot/recipe-harness` 0.11.0 - Result: 201 passed, 0 failed, 136,200 ms; no recovery or runtime mutation - Safety: all ten mutating setup/cleanup executions reported `requested: testnet`, `isTestnet: true`, and finished with zero matching orders and positions - Coverage: attributed screen views; search query, result, and abandonment; discovery controls; trade/open and close; single and batch cancellation; TP/SL; margin dialogs; position reversal; order consideration and abandonment; displayed error screens - Assertions use exact event totals plus required-property matches, so duplicate emissions fail rather than passing on presence alone - Review: [report](https://github.com/abretonc7s/mm-extension-farm-artifacts/blob/461d55a9e1406dfd57695bf5b6ff84d25ff991bb/fixes/45119/analytics-lifecycle/report.md), [summary](https://github.com/abretonc7s/mm-extension-farm-artifacts/blob/461d55a9e1406dfd57695bf5b6ff84d25ff991bb/fixes/45119/analytics-lifecycle/summary.json), [trace](https://github.com/abretonc7s/mm-extension-farm-artifacts/blob/461d55a9e1406dfd57695bf5b6ff84d25ff991bb/fixes/45119/analytics-lifecycle/trace.json), and [SHA-256 checksums](https://github.com/abretonc7s/mm-extension-farm-artifacts/blob/461d55a9e1406dfd57695bf5b6ff84d25ff991bb/fixes/45119/analytics-lifecycle/SHA256SUMS) The trace proves analytics consumption after routing inside the running Extension. It deliberately does not claim external signed-deeplink parsing, canonicalization, or security behavior. </details> ## **Validation** - 18 affected Perps suites / 730 tests passed with no console-baseline violations. - `yarn lint:changed:fix` passed with zero errors. - `yarn lint:tsc` passed. - Final diff audit found no files under `shared/lib/deep-links`, no `handlerSearchParams: original`, no `withDeeplinkAttribution`, and no original/unsigned handler parameter access. - The composed live recipe passed 201/201 nodes against this exact product head. ## **Pre-merge author checklist** - [x] I have followed MetaMask Contributor Docs and MetaMask Extension Coding Standards. - [x] I have completed the PR template to the best of my ability. - [x] I have included tests where applicable. - [x] I have documented code using JSDoc where applicable. - [x] I have applied the right labels. Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I have manually tested the PR. - [ ] I confirm that this PR addresses the applicable acceptance criteria and includes the necessary testing evidence. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches MetaMetrics event shapes and ownership across many Perps flows; mis-deduping or wrong status/attribution would skew analytics but does not change trading or auth logic. > > **Overview** > Re-lands Perps analytics alignment with `@metamask/perps-controller`: **`shared/constants/perps-events`** now imports the controller contract (with Extension aliases), new MetaMetrics funnel events are registered, and the unused **`tutorial`** locale key is removed. > > **Background:** `perpsSetAttributionContext` is exposed and `mergeAttributionContext` is wired through `createPerpsInfrastructure` so controller-emitted transaction events pick up stored UTM fields before MetaMetrics. Tests consolidate on **`test/mocks/metamask-perps-controller.js`**. > > **UI:** `PerpsAttributionProvider` / **`usePerpsAttribution`** replace `buildPerpsVipTrackingData` and attach **`trackingData`** (fees, VIP, `hlFeeRate`, flow attribution) on cancel, close, flip, and TP/SL. Duplicate client **transaction** analytics are removed where the controller owns submitted/terminal events; gaps remain for transport throws, margin `{ success: false }`, batch close counts, **`trackPerpsErrorScreenViewed`**, geo-block screen views, **`usePerpsAbandonOrderTracking`**, and order-size **`input_method`** plumbing. **`derivePerpsTradeAction`** keeps considered vs executed `action` consistent (including omitting reduce-only opposite-side orders). > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit b5e316c. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
n3ps
pushed a commit
to MetaMask/metamask-extension
that referenced
this pull request
Aug 6, 2026
## **Description** Re-lands the analytics-contract consumption from #44324 after its revert in #45087, without the deeplink-attribution route changes. This keeps the controller-owned analytics contract, event ownership and deduplication, UI funnel/error/abandonment tracking, and attribution consumption. It does not change any deeplink route handler, add `handlerSearchParams: original`, or forward original/unsigned parameters. The signed-only attribution follow-up is proposed separately in #45123 for focused Security review. ## **Changelog** CHANGELOG entry: null ## **Related issues** Refs: #44324, #45087, MetaMask/core#9311, MetaMask/core#9471 Follow-up: [TAT-3646 — restore signed Perps deeplink attribution](https://consensyssoftware.atlassian.net/browse/TAT-3646) ## **Manual testing steps** 1. Build and start the Extension with Perps enabled. 2. Open Perps and navigate through the market list, market detail, and order entry screens. 3. With analytics consent enabled, complete trade, close, cancel, TP/SL, and margin flows. 4. Confirm each submitted and terminal transaction event is emitted once, and confirm search, abandonment, filter, sort, watchlist, and displayed-error events include their expected properties. <!-- ## **Screenshots/Recordings** Fresh exact-head live analytics evidence will be attached after the production build and composable recipe run complete. This evidence validates the analytics contract only; it is not deeplink-security evidence. --> ## **Live recipe evidence** The reusable recipe is merged at [MetaMask/experimental-metamask-recipe-perps@c7cd115](https://github.com/MetaMask/experimental-metamask-recipe-perps/blob/c7cd11526ba85c2601463ede7f8cdf9697e8bde0/recipes/perps/analytics-lifecycle.extension.recipe.json). The scrubbed, checksummed evidence is published at [abretonc7s/mm-extension-farm-artifacts@461d55a](https://github.com/abretonc7s/mm-extension-farm-artifacts/tree/461d55a9e1406dfd57695bf5b6ff84d25ff991bb/fixes/45119/analytics-lifecycle). <details> <summary>201/201-node live HyperLiquid testnet proof</summary> ```sh RECIPE_LIBRARY_PATH="perps=/path/to/experimental-metamask-recipe-perps" \ mm-harness run perps.analytics-lifecycle \ --adapter extension \ market=BTC market_query=btc ``` - Product head: `d5e5a0184f01dd5fb099313060f99399327d9208` - Recipe source: merged commit `c7cd11526ba85c2601463ede7f8cdf9697e8bde0` - Runner: `@metamask/recipe-runner` source commit `fea899ce2716fd54cced5b58652025930d42fbf9` - Runtime: `@farmslot/recipe-harness` 0.11.0 - Result: 201 passed, 0 failed, 136,200 ms; no recovery or runtime mutation - Safety: all ten mutating setup/cleanup executions reported `requested: testnet`, `isTestnet: true`, and finished with zero matching orders and positions - Coverage: attributed screen views; search query, result, and abandonment; discovery controls; trade/open and close; single and batch cancellation; TP/SL; margin dialogs; position reversal; order consideration and abandonment; displayed error screens - Assertions use exact event totals plus required-property matches, so duplicate emissions fail rather than passing on presence alone - Review: [report](https://github.com/abretonc7s/mm-extension-farm-artifacts/blob/461d55a9e1406dfd57695bf5b6ff84d25ff991bb/fixes/45119/analytics-lifecycle/report.md), [summary](https://github.com/abretonc7s/mm-extension-farm-artifacts/blob/461d55a9e1406dfd57695bf5b6ff84d25ff991bb/fixes/45119/analytics-lifecycle/summary.json), [trace](https://github.com/abretonc7s/mm-extension-farm-artifacts/blob/461d55a9e1406dfd57695bf5b6ff84d25ff991bb/fixes/45119/analytics-lifecycle/trace.json), and [SHA-256 checksums](https://github.com/abretonc7s/mm-extension-farm-artifacts/blob/461d55a9e1406dfd57695bf5b6ff84d25ff991bb/fixes/45119/analytics-lifecycle/SHA256SUMS) The trace proves analytics consumption after routing inside the running Extension. It deliberately does not claim external signed-deeplink parsing, canonicalization, or security behavior. </details> ## **Validation** - 18 affected Perps suites / 730 tests passed with no console-baseline violations. - `yarn lint:changed:fix` passed with zero errors. - `yarn lint:tsc` passed. - Final diff audit found no files under `shared/lib/deep-links`, no `handlerSearchParams: original`, no `withDeeplinkAttribution`, and no original/unsigned handler parameter access. - The composed live recipe passed 201/201 nodes against this exact product head. ## **Pre-merge author checklist** - [x] I have followed MetaMask Contributor Docs and MetaMask Extension Coding Standards. - [x] I have completed the PR template to the best of my ability. - [x] I have included tests where applicable. - [x] I have documented code using JSDoc where applicable. - [x] I have applied the right labels. Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I have manually tested the PR. - [ ] I confirm that this PR addresses the applicable acceptance criteria and includes the necessary testing evidence. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches MetaMetrics event shapes and ownership across many Perps flows; mis-deduping or wrong status/attribution would skew analytics but does not change trading or auth logic. > > **Overview** > Re-lands Perps analytics alignment with `@metamask/perps-controller`: **`shared/constants/perps-events`** now imports the controller contract (with Extension aliases), new MetaMetrics funnel events are registered, and the unused **`tutorial`** locale key is removed. > > **Background:** `perpsSetAttributionContext` is exposed and `mergeAttributionContext` is wired through `createPerpsInfrastructure` so controller-emitted transaction events pick up stored UTM fields before MetaMetrics. Tests consolidate on **`test/mocks/metamask-perps-controller.js`**. > > **UI:** `PerpsAttributionProvider` / **`usePerpsAttribution`** replace `buildPerpsVipTrackingData` and attach **`trackingData`** (fees, VIP, `hlFeeRate`, flow attribution) on cancel, close, flip, and TP/SL. Duplicate client **transaction** analytics are removed where the controller owns submitted/terminal events; gaps remain for transport throws, margin `{ success: false }`, batch close counts, **`trackPerpsErrorScreenViewed`**, geo-block screen views, **`usePerpsAbandonOrderTracking`**, and order-size **`input_method`** plumbing. **`derivePerpsTradeAction`** keeps considered vs executed `action` consistent (including omitting reduce-only opposite-side orders). > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit b5e316c. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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.

Explanation
Perps analytics instrumentation was fragmented: event names, property keys, and value enums
were partly mirrored in clients, and several ticketed properties/events (submitted status,
discovery attribution, UTM context,
hl_fee_rate,bulk_action_id, flipmetamask_fee,close
leverage) had no home in core. This made client wiring inconsistent and blocked anumber of analytics tickets.
This PR consolidates the analytics contract and the transaction pipeline inside
@metamask/perps-controllerso Mobile and Extension can adopt a single source of truth infollow-up tickets after the package is published.
Contract (
constants+types):PerpsAnalyticsEventmembers —Perp Transaction Considered,Perp Trade Quote Received,Perp Search Query,Perp Search Result Tapped,Perp Search Abandoned— and no others (nine existing names are unchanged).PERPS_EVENT_PROPERTYkeys (entry point / discovery, UTM,watchlisted,hl_fee_rate,bulk_action_id,environment_type, order-funnel/quote,search, sort/filter,
time_on_screen_ms) and newPERPS_EVENT_VALUEentries(
INTERACTION_TYPE,ACTION.abandon_order,BUTTON_CLICKED,SCREEN_TYPE).PerpsAttributionContextandsetAttributionContext/getAttributionContext/clearAttributionContext/mergeAttributionContextonPerpsController(with messengeractions) for transient, in-memory UTM attribution propagation.
Pipeline (
TradingService):status: 'submitted'event before the provider round-trip for trade, close,cancel and risk-management operations.
metamask_feeon successful flip trades; addsleverageto close events; addshl_fee_rateto trade/close events when available (omitted otherwise); generates abulk_action_idfor batch close/cancel and attaches it to per-item and summary events;propagates
entry_point/discovery_source/perp_discovery_sourceontotrade/close/cancel/risk events (
sourceonTPSLTrackingDatais now deprecated).Package effects: new exports from
@metamask/perps-controller(PerpsAttributionContext,three new messenger action types) and from
@metamask/perps-controller/constantsand/types. All changes are additive; no events are renamed or removed.Validation:
eventNames.test.ts(60) andTradingService.test.ts(93, incl. 12 new) pass;PerpsController.state.test.ts(43, incl. 6 attribution cases), andPerpsController.trading.test.ts+PerpsController.operations.test.ts(71) pass; fullmonorepo
yarn buildis green (zero TS errors); changed-file ESLint andchangelog:validateare clean. The headless core smoke recipe could not run because the recipe runner's harness
dependency (
@farmslot/recipe-harness) is not provisioned in this environment; controllerwiring is instead covered by the clean build and passing controller tests.
References
TAT-3142, TAT-3144, TAT-3146, TAT-3147, TAT-3148, TAT-3149, TAT-3150, TAT-3151, TAT-3202,
TAT-3335 (key only)
Checklist
Note
Medium Risk
Adds additive analytics surface and changes when/how many MetaMetrics events fire for trades (submitted + terminal), which can affect dashboards; trading logic itself is mostly instrumentation.
Overview
Centralizes Perps analytics in
@metamask/perps-controllerso Mobile/Extension can stop mirroring event names and property keys locally.Contract: Adds five
PerpsAnalyticsEventvalues (transaction considered, trade quote, search query/result/abandon), many newPERPS_EVENT_PROPERTY/PERPS_EVENT_VALUEentries (discovery, UTM, order funnel, search, sort/filter), extendsTrackingData/TPSLTrackingDataand optionaltrackingDataonCancelOrderParams, and deprecatesTPSLTrackingData.sourcein favor of entry/discovery fields.Attribution: Introduces in-memory
PerpsAttributionContextwithset/get/clear/mergeAttributionContextonPerpsController(plus messenger actions) for UTM propagation onto events.TradingServicepipeline: Emitsstatus: submittedbefore provider calls for place/close/cancel/TP-SL and flip; enriches terminal events with discovery fields, optionalhl_fee_rate, closeleverage, flipmetamask_fee, andbulk_action_idon batch close/cancel; ensures terminal outcomes when flip fails without throw or close runs without a local position.Tests cover the new constants, attribution APIs, and the consolidated analytics behavior.
Reviewed by Cursor Bugbot for commit 155e0c5. Bugbot is set up for automated code reviews on this repo. Configure here.