Skip to content

feat: implement wallet-side analytics for SDKConnectV2 / MWP#27864

Merged
adonesky1 merged 35 commits into
mainfrom
jc/WAPI-1348
Apr 17, 2026
Merged

feat: implement wallet-side analytics for SDKConnectV2 / MWP#27864
adonesky1 merged 35 commits into
mainfrom
jc/WAPI-1348

Conversation

@ffmcgee725
Copy link
Copy Markdown
Member

@ffmcgee725 ffmcgee725 commented Mar 24, 2026

Description

  • Fire DEEP_LINK_USED (MetaMetrics) for every MWP deeplink received, with route: 'mmc-mwp' and Branch.io app-installation detection
  • Track wallet-side MWP connection lifecycle events (Remote Connection Request Received, Remote Connection Request Failed) through the standard MetaMetrics / AnalyticsController pipeline
  • Track reconnect attempts (handleSimpleDeeplink) with found_in_store to surface silent store-miss failures

Motivation

Dapp-side events (mmconnect_connection_initiated with transport_type: 'mwp') tell us how many dapps tried to connect via MWP, but not how many wallets actually received them. The drop-off between dapp-initiated and wallet-received (silent failures, iOS routing issues) is completely invisible without wallet-side instrumentation. MWP adoption rate — the key signal for deprecating SDKv1 — can't be tracked from the wallet's perspective.

Changes

Part A — DEEP_LINK_USED for MWP deeplinks

File Change
app/core/DeeplinkManager/types/deepLinkAnalytics.types.ts Add MMC_MWP = 'mmc_mwp' to DeepLinkRoute enum
app/core/DeeplinkManager/util/deeplinks/deepLinkAnalytics.ts Register MMC_MWP in the route-extractor map
app/core/DeeplinkManager/handlers/legacy/handleDeeplink.ts Fire DEEP_LINK_USED asynchronously before SDKConnectV2.handleMwpDeeplink() — never blocks the WebSocket handshake

Part B — Wallet-side connection events via MetaMetrics

All wallet-side MWP events are routed through the standard analytics.trackEvent() / AnalyticsController pipeline. This means:

  • Events appear in the mobile app's Segment source under the user's analyticsId
  • Opt-in / opt-out is enforced by the controller — no manual consent check needed
  • "Delete MetaMetrics Data" covers MWP events (same source + same identity)
  • Events automatically include metamask-mobile-globals default properties

The per-session remote_session_id (previously anon_id) is preserved as an event property so dapp↔wallet join analysis continues to work.

File Change
app/core/Analytics/MetaMetrics.events.ts Add REMOTE_CONNECTION_REQUEST_RECEIVED and REMOTE_CONNECTION_REQUEST_FAILED to the EVENT_NAME enum and events object
app/core/SDKConnectV2/services/connection-registry.ts Instrument handleConnectDeeplink and handleSimpleDeeplink with analytics.trackEvent() calls using AnalyticsEventBuilder and MetaMetricsEvents
app/core/SDKConnectV2/services/connection-registry.test.ts Add analytics mock and assertions covering received/failed events, found_in_store, and failure_reason

Deleted (superseded by inline analytics.trackEvent() calls):

  • app/core/SDKConnectV2/services/v2-analytics.ts — no longer needed; removed the custom HTTP POST to mm-sdk-analytics.api.cx.metamask.io/v2/events and all associated types
  • app/core/SDKConnectV2/services/v2-analytics.test.ts — tests moved to connection-registry.test.ts

Event map

Trigger Event Extra properties
handleConnectDeeplink() entry Remote Connection Request Received sdk_version, sdk_platform
handleConnectDeeplink() catch Remote Connection Request Failed failure_reason
handleSimpleDeeplink() found Remote Connection Request Received found_in_store: true, sdk_version, sdk_platform
handleSimpleDeeplink() not found Remote Connection Request Received found_in_store: false

All events include remote_session_id (per-session UUID shared with the dapp) and platform: 'mobile'.

Test plan

  • Open an MWP deeplink (metamask://connect/mwp?p=...&c=1) — verify DEEP_LINK_USED appears in MetaMetrics with route: sdk_mwp
  • Complete a new MWP connection — verify Remote Connection Request Received arrives in MetaMetrics with sdk_version, sdk_platform, and remote_session_id
  • Force a connection error (e.g. malformed payload) — verify Remote Connection Request Failed fires with failure_reason
  • Open a reconnect deeplink (metamask://connect/mwp?id=...) for an existing session — verify Remote Connection Request Received with found_in_store: true
  • Open a reconnect deeplink with a stale/unknown ID — verify Remote Connection Request Received with found_in_store: false
  • Disable MetaMetrics in Settings — verify zero MWP events are tracked (enforced by AnalyticsController)
  • Confirm no PII or sensitive values in any event payload (addresses, keys, etc.)
  • Confirm no direct HTTP requests to mm-sdk-analytics.api.cx.metamask.io (v2 relay fully removed)

Changelog

CHANGELOG entry: null

Related issues

Fixes:

Schema PRs:

Manual testing steps

Feature: MWP wallet-side analytics

  Scenario: New MWP connection tracks received event through MetaMetrics
    Given the user has MetaMetrics enabled
    When a dapp initiates a new MWP connection via deeplink
    Then a "Remote Connection Request Received" event appears in MetaMetrics
    And the event includes remote_session_id, sdk_version, sdk_platform, and platform properties

  Scenario: Failed MWP connection tracks failure event with reason
    Given the user has MetaMetrics enabled
    When a dapp sends a malformed MWP deeplink
    Then a "Remote Connection Request Failed" event appears in MetaMetrics
    And the event includes a failure_reason property

  Scenario: Reconnect deeplink tracks found_in_store status
    Given the user has MetaMetrics enabled and has an existing MWP session
    When a reconnect deeplink arrives for a known session ID
    Then a "Remote Connection Request Received" event fires with found_in_store: true

  Scenario: Reconnect deeplink for unknown session tracks store miss
    Given the user has MetaMetrics enabled
    When a reconnect deeplink arrives for an unknown session ID
    Then a "Remote Connection Request Received" event fires with found_in_store: false

  Scenario: Opted-out user generates no MWP events
    Given the user has MetaMetrics disabled
    When any MWP deeplink is received
    Then no analytics events are tracked (enforced by AnalyticsController)

Screenshots/Recordings

Before

After

Pre-merge author checklist

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
  • I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.

Note

Medium Risk
Touches early deeplink handling and connection establishment error paths to emit analytics, which could impact connection reliability if event construction is wrong (though calls are guarded/fire-and-forget).

Overview
Adds wallet-side analytics for MWP/SDKConnectV2 flows: handleDeeplink now asynchronously emits MetaMetricsEvents.DEEP_LINK_USED for MWP deeplinks (including Branch-based was_app_installed) while still fast-pathing to SDKConnectV2.

In ConnectionRegistry, MWP connect and reconnect deeplinks now track MetaMetrics events for request received (including remote_session_id, transport_type, and optional SDK metadata + found_in_store) and request failed (with failure_reason), with analytics wrapped to never throw. The PR also extends the deep link route taxonomy with DeepLinkRoute.MMC_MWP and adds/updates unit tests to assert the new tracking behavior.

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

@ffmcgee725 ffmcgee725 requested review from a team as code owners March 24, 2026 13:08
@metamaskbot metamaskbot added the team-wallet-integrations Wallet Integrations team label Mar 24, 2026
@github-actions github-actions Bot added size-M risk-medium Moderate testing recommended · Possible bug introduction risk labels Mar 24, 2026
Comment thread app/core/SDKConnectV2/services/connection.ts Outdated
Comment thread app/core/SDKConnectV2/services/connection.ts Outdated
Comment thread app/core/DeeplinkManager/handlers/legacy/handleDeeplink.ts
Comment thread app/core/DeeplinkManager/types/deepLinkAnalytics.types.ts Outdated
Comment thread app/core/SDKConnectV2/services/connection-registry.ts Outdated
Comment thread app/core/SDKConnectV2/services/connection-registry.ts Outdated
Comment on lines +4 to +10
// class supports the `mobile/sdk-connect-v2` namespace. Currently
// `Analytics.track()` is hard-typed to `MMConnectPayload` (namespace
// `metamask/connect`). The upstream work is tracked in:
// https://consensyssoftware.atlassian.net/browse/WAPI-1350

const V2_ANALYTICS_ENDPOINT =
'https://mm-sdk-analytics.api.cx.metamask.io/v2/events';
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.

I think we can't/shouldn't be piping these through this proxy endpoint since it fully bypasses the analytics consent gate we apply to all other analytics in the wallet. I suppose we could read that state and still pass it through this same proxy to keep our events colocated 🤔 If you have the time would you mind pinging the @MetaMask/mobile-platform devs to see what they think on this? Maybe @NicolasMassart and/or @Cal-L ?

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.

Why SDK doesn't initialize using the same system as controllers in mobile app? It would benefit from common analytics id and the option consents data

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@NicolasMassart I wouldn't be able to tell you, when our team inherited code ownership for this, this structure was already in place as such, where ConnectionRegistry is not a part of the Engine controller system and is instead a standalone singleton created directly in app/core/SDKConnectV2/index.ts.

Would you be okay if for this PR, we follow an approach where we check consent in v2-analytics.ts before sending the payload ?

We can add something to our backlog for making ConnectionRegistry a proper controller where we :

  • Register SDKConnectV2 in CONTROLLER_MESSENGERS
  • Create a messenger definition that delegates AnalyticsController:trackEvent
  • Change the singleton initialization to go through initModularizedControllers

cc.: @adonesky1

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

5af23b6 fixes so we don't bypass analytics consent gate anymore. Followed the "lightweight" approach stated in the prior 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.

Yes please add a followup issue for this, we have to pipe all analytics through the analytics-controller.

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.

Or you can simply call analytics.trackEvent in trackWalletEvent for now, and refactor later. But getting rid of this separate endpoint here would be great, it's just a one line change

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@NicolasMassart @adonesky1 tracking follow up here

@github-actions github-actions Bot added risk-medium Moderate testing recommended · Possible bug introduction risk and removed risk-medium Moderate testing recommended · Possible bug introduction risk labels Mar 25, 2026
Comment thread app/core/SDKConnectV2/services/connection-registry.ts Outdated
@github-actions github-actions Bot added risk-medium Moderate testing recommended · Possible bug introduction risk and removed risk-medium Moderate testing recommended · Possible bug introduction risk labels Mar 25, 2026
Comment thread app/core/SDKConnectV2/services/v2-analytics.ts Outdated
@github-actions github-actions Bot added risk-low Low testing needed · Low bug introduction risk and removed risk-medium Moderate testing recommended · Possible bug introduction risk labels Mar 25, 2026
Comment thread app/core/SDKConnectV2/services/v2-analytics.ts Outdated
@github-actions github-actions Bot removed the risk-low Low testing needed · Low bug introduction risk label Mar 25, 2026
@github-actions github-actions Bot added the risk-low Low testing needed · Low bug introduction risk label Apr 16, 2026
adonesky1
adonesky1 previously approved these changes Apr 16, 2026
@github-actions github-actions Bot added risk-low Low testing needed · Low bug introduction risk and removed risk-low Low testing needed · Low bug introduction risk labels Apr 16, 2026
jiexi
jiexi previously approved these changes Apr 16, 2026
@github-actions github-actions Bot added risk-low Low testing needed · Low bug introduction risk and removed risk-low Low testing needed · Low bug introduction risk labels Apr 16, 2026
Comment thread app/core/Analytics/MetaMetrics.events.ts Outdated
@github-project-automation github-project-automation Bot moved this to Needs dev review in PR review queue Apr 16, 2026
…merge

The main merge brought in PR #28322's existing REMOTE_CONNECTION_REQUEST_RECEIVED
entries, which collided with the ones this branch had added. Consolidate by
keeping main's originals in their groups and moving REMOTE_CONNECTION_REQUEST_FAILED
alongside them, fixing TS2300/TS1117 errors in lint:tsc.
@adonesky1 adonesky1 dismissed stale reviews from jiexi and themself via 0e612b5 April 16, 2026 19:00
adonesky1
adonesky1 previously approved these changes Apr 16, 2026
@github-actions github-actions Bot added risk-low Low testing needed · Low bug introduction risk and removed risk-low Low testing needed · Low bug introduction risk labels Apr 16, 2026
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0e612b5. Configure here.

Comment thread app/core/DeeplinkManager/handlers/legacy/handleDeeplink.ts Outdated
@github-actions github-actions Bot added risk-low Low testing needed · Low bug introduction risk and removed risk-low Low testing needed · Low bug introduction risk labels Apr 17, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

  • Selected E2E tags: SmokeNetworkExpansion, SmokeMultiChainAPI, SmokeWalletPlatform
  • Selected Performance tags: None (no tests recommended)
  • Risk Level: low
  • AI Confidence: 82%
click to see 🤖 AI reasoning details

E2E Test Selection:
The changes are analytics-only additions to the MWP (MetaMask Wallet Protocol) deeplink and SDK connection flows:

  1. MetaMetrics.events.ts: Added REMOTE_CONNECTION_REQUEST_FAILED event - purely additive, no functional change.
  2. deepLinkAnalytics.types.ts: Added MMC_MWP to DeepLinkRoute enum - additive type change.
  3. deepLinkAnalytics.ts: Added extractMmcMwpProperties handler - additive analytics utility.
  4. handleDeeplink.ts: Added async analytics tracking (DEEP_LINK_USED) when MWP deeplinks are processed. The analytics call is fire-and-forget and explicitly designed not to block SDKConnectV2.handleMwpDeeplink().
  5. connection-registry.ts: Added fire-and-forget analytics tracking to MWP connection establishment and failure paths. The trackMwpEvent helper swallows errors to ensure analytics never blocks connection flows.
  6. Test files: Unit tests validating the new analytics behavior.

Risk Assessment: Low risk because:

  • All analytics calls are async/fire-and-forget with explicit error swallowing
  • Core connection logic (SDKConnectV2.handleMwpDeeplink, connection establishment) is unchanged
  • No UI components, navigation, confirmations, or transaction flows are modified
  • The changes are in app/core/ (critical path) but only add observability

Tag Selection Rationale:

  • SmokeNetworkExpansion: MWP deeplinks are used for dApp connections (Solana Wallet Standard, multi-chain provider). The deeplink handler and connection registry are directly in this flow. Need to verify the analytics additions don't inadvertently affect connection establishment.
  • SmokeMultiChainAPI: The connection-registry.ts is part of the CAIP-25 session management infrastructure. The handleConnectDeeplink and handleSimpleDeeplink methods now have analytics calls that should be verified not to interfere with session creation/management.
  • SmokeWalletPlatform: Covers EVM provider event handling for dApp communication, which uses the same deeplink infrastructure. Also covers analytics event tracking for wallet lifecycle.

Tags NOT selected: SmokeConfirmations, SmokeTrade, SmokeAccounts, SmokeRamps, SmokeCard, SmokePerps, SmokePredictions, SmokeIdentity, SmokeSeedlessOnboarding, FlaskBuildTests - none of these flows are touched by the changes.

Performance Test Selection:
The changes are purely analytics instrumentation additions (fire-and-forget async calls) to the MWP deeplink and connection registry flows. No UI rendering, list components, data loading, state management, or critical user flow performance characteristics are affected. The analytics calls are explicitly designed to be non-blocking and swallow errors, so they cannot impact measured performance metrics.

View GitHub Actions results

@sonarqubecloud
Copy link
Copy Markdown

@github-actions
Copy link
Copy Markdown
Contributor

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

@adonesky1 adonesky1 added this pull request to the merge queue Apr 17, 2026
@github-project-automation github-project-automation Bot moved this from Needs dev review to Review finalised - Ready to be merged in PR review queue Apr 17, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-7.75.0 Issue or pull request that will be included in release 7.75.0 risk-low Low testing needed · Low bug introduction risk size-M team-wallet-integrations Wallet Integrations team

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

7 participants