feat: integrate analytics controller#42885
Conversation
|
CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes. |
✨ Files requiring CODEOWNER review ✨👨🔧 @MetaMask/core-extension-ux (9 files, +24 -22)
👨🔧 @MetaMask/extension-platform (4 files, +10 -7)
🕵️ @MetaMask/extension-privacy-reviewers (1 files, +3 -1)
💎 @MetaMask/metamask-assets (1 files, +2 -2)
📜 @MetaMask/policy-reviewers (8 files, +64 -0)
Tip Follow the policy review process outlined in the LavaMoat Policy Review Process doc before expecting an approval from Policy Reviewers. 🧪 @MetaMask/qa (5 files, +13 -8)
📈 @MetaMask/ramp (2 files, +2 -2)
🔄 @MetaMask/swaps-engineers (1 files, +37 -3)
👨🔧 @MetaMask/wallet-integrations (3 files, +9 -7)
🔐 @MetaMask/web3auth (5 files, +50 -25)
|
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Builds ready [2b95db2]
⚡ Performance Benchmarks (Total: 🟢 12 pass · 🟡 13 warn · 🔴 0 fail)
Bundle size diffs [🚨 Warning! Bundle size has increased!]
|
Builds ready [2e57540]
⚡ Performance Benchmarks (Total: 🟢 13 pass · 🟡 2 warn · 🔴 0 fail)
Bundle size diffs [🚨 Warning! Bundle size has increased!]
|
Builds ready [27c93f3]
⚡ Performance Benchmarks (Total: 🟢 23 pass · 🟡 2 warn · 🔴 0 fail)
Bundle size diffs [🚨 Warning! Bundle size has increased!]
|
Builds ready [08a46b1]
⚡ Performance Benchmarks (Total: 🟢 23 pass · 🟡 1 warn · 🔴 0 fail)
Bundle size diffs [🚨 Warning! Bundle size has increased!]
|
itsyoboieltr
left a comment
There was a problem hiding this comment.
I saw in multiple places that we are using both analyticsId and metametricsId to refer to the same value. Also some places refer to Metametrics, some refer to Analytics. I wonder if this could be avoided somehow? Makes the code really confusing, unless someone already knows Analytics and Metametrics are the same thing.
| } | ||
| merge(analyticsController, analyticsPatch); | ||
| } | ||
|
|
There was a problem hiding this comment.
will the tests be eventually migrated? I do not know if we want to keep the legacy syntax around forever
…ller-extension-integration
|
@itsyoboieltr yes that's confusing and I'm really tempted to fully get rid of I ended up not doing it in this PR, just for the sake of keeping the size of the PR "reasonable". Do you think that iterative approach makes sense? PS: That said, I'll do a review tomorrow to fix examples you highlighted, where the terms |
The analytics-controller rework stopped whitelisting metaMetricsId/ participateInMetaMetrics in the Sentry state mask (they are masked now), and AnalyticsController.eventQueue is absent in the pre-init/migration- error state since it is only populated by migration 212 when there is data to migrate. Update the affected snapshots accordingly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builds ready [172d4f5]
⚡ Performance Benchmarks (Total: 🟢 23 pass · 🟡 2 warn · 🔴 0 fail)
Bundle size diffs [🚨 Warning! Bundle size has increased!]
|
The AnalyticsController surfaces analyticsId and optedIn (both flagged includeInStateLogs), and completedMetaMetricsOnboarding now appears in the flattened metamask state logs. Add them to the expected type map. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builds ready [1f04572]
⚡ Performance Benchmarks (Total: 🟢 22 pass · 🟡 3 warn · 🔴 0 fail)
Bundle size diffs [🚨 Warning! Bundle size has increased!]
|
setParticipateInMetaMetrics(null) clears completedMetaMetricsOnboarding but intentionally leaves AnalyticsController opted in (to preserve the queued events). #canSubmitAnalytics only checked optedIn, so events could still be sent after a participation reset (e.g. onboarding welcome login). Gate submission on completedMetaMetricsOnboarding as well, rather than opting out on null (which would clear the queue). Adds a regression test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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 482558b. Configure here.
Reverts commits 0cb4ee1 ("fix: preserve analytics queue on metrics reset") and 482558b ("fix: block analytics submission when metrics participation is null"). setParticipateInMetaMetrics(null) once again calls AnalyticsController:optOut (else branch), so optedIn is cleared on reset and #canSubmitAnalytics no longer needs the completedMetaMetricsOnboarding gate. This blocks tracking after a null reset at the cost of clearing the queued events. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builds ready [482558b]
⚡ Performance Benchmarks (Total: 🟢 22 pass · 🟡 3 warn · 🔴 0 fail)
Bundle size diffs [🚨 Warning! Bundle size has increased!]
|
Builds ready [5d73b11]
⚡ Performance Benchmarks (Total: 🟢 23 pass · 🟡 2 warn · 🔴 0 fail)
Bundle size diffs [🚨 Warning! Bundle size has increased!]
|
There was a problem hiding this comment.
Pull request overview
This PR integrates @metamask/analytics-controller into the extension and begins migrating persisted/compatibility state away from MetaMetricsController fields (metaMetricsId, participateInMetaMetrics, segmentApiCalls) toward AnalyticsController (analyticsId, optedIn, eventQueue) while keeping legacy UI fields derivable for a phased rollout.
Changes:
- Add
AnalyticsControllerto background initialization/messaging and persist/backup flows, including a new Segment platform adapter. - Introduce migration
212to move legacy MetaMetrics consent/ID/queue state intoAnalyticsController+completedMetaMetricsOnboarding. - Update UI selectors, actions, fixtures, and tests to use
analyticsId/optedIn/completedMetaMetricsOnboardingwhile maintaining compatibility accessors.
Reviewed changes
Copilot reviewed 114 out of 117 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| yarn.lock | Add analytics-controller dependency lock |
| package.json | Add @metamask/analytics-controller dependency |
| attribution.txt | Add analytics-controller attribution entry |
| lavamoat/webpack/mv2/main/policy.json | Allow analytics-controller deps (MV2 main) |
| lavamoat/webpack/mv2/flask/policy.json | Allow analytics-controller deps (MV2 flask) |
| lavamoat/webpack/mv2/experimental/policy.json | Allow analytics-controller deps (MV2 experimental) |
| lavamoat/webpack/mv2/beta/policy.json | Allow analytics-controller deps (MV2 beta) |
| lavamoat/browserify/main/policy.json | Allow analytics-controller deps (browserify main) |
| lavamoat/browserify/flask/policy.json | Allow analytics-controller deps (browserify flask) |
| lavamoat/browserify/experimental/policy.json | Allow analytics-controller deps (browserify experimental) |
| lavamoat/browserify/beta/policy.json | Allow analytics-controller deps (browserify beta) |
| shared/lib/generate-metametrics-id.ts | New helper to generate analytics ID |
| shared/constants/metametrics.ts | Remove legacy actionId/options types |
| shared/types/background.ts | Add AnalyticsControllerState to merged/flattened typing |
| shared/lib/stores/persistence-manager.ts | Include AnalyticsController in backups; refactor getBackup |
| shared/lib/stores/persistence-manager.test.ts | Add coverage for backing up AnalyticsController |
| app/scripts/migrations/index.js | Register migration 212 |
| app/scripts/migrations/212.ts | Migrate MMC consent/id/queue into AnalyticsController + onboarding flag |
| app/scripts/migrations/212.test.ts | Add migration 212 unit tests |
| app/scripts/messenger-client-init/controller-list.ts | Include AnalyticsController in messenger client types |
| app/scripts/messenger-client-init/analytics-controller-init.ts | Initialize AnalyticsController with adapter + generated ID |
| app/scripts/messenger-client-init/messengers/analytics-controller-messenger.ts | Add AnalyticsController messenger factory |
| app/scripts/messenger-client-init/messengers/index.ts | Register AnalyticsController messenger factory |
| app/scripts/metamask-controller.js | Wire AnalyticsController; derive legacy participateInMetaMetrics/metaMetricsId in getState() |
| app/scripts/metamask-controller.test.js | Update init-state expectations for AnalyticsController |
| app/scripts/controllers/analytics/platform-adapter.ts | New Segment platform adapter for AnalyticsController |
| app/scripts/controllers/analytics/platform-adapter.test.ts | Unit tests for platform adapter behavior |
| app/scripts/controllers/metametrics-controller.ts | Delegate tracking to AnalyticsController; remove Segment-owned queue/id fields |
| app/scripts/controllers/metametrics-controller-method-action-types.ts | Update exposed actions (remove id generation; restructure track/identify/page types) |
| app/scripts/messenger-client-init/metametrics-controller-init.ts | Remove Segment injection; document delegation to AnalyticsController |
| app/scripts/messenger-client-init/metametrics-controller-init.test.ts | Update init test expectations (no Segment param) |
| app/scripts/messenger-client-init/messengers/metametrics-controller-messenger.ts | Delegate AnalyticsController actions through MetaMetrics messenger |
| app/scripts/messenger-client-init/messengers/metametrics-data-deletion-controller-messenger.ts | Switch deletion controller dependency to AnalyticsController:getState |
| app/scripts/controllers/metametrics-data-deletion/metametrics-data-deletion.ts | Use AnalyticsController analyticsId for deletion tasks |
| app/scripts/controllers/metametrics-data-deletion/metametrics-data-deletion.test.ts | Update deletion controller tests for AnalyticsController |
| app/scripts/messenger-client-init/profile-metrics-controller-init.ts | Use AnalyticsController optedIn for profile metrics gating |
| app/scripts/messenger-client-init/seedless-onboarding/oauth-service-init.ts | Derive consent via completed-onboarding flag + AnalyticsController.optedIn |
| app/scripts/lib/segment/index.ts | Update docs + mock behavior; keep payload cloning and callback semantics |
| app/scripts/lib/segment/tests/segment-shim.test.ts | Update segment shim tests for track/identify/page forwarding + cloning |
| app/scripts/lib/segment/early-segment-tracking.ts | Remove old early tracking module |
| app/scripts/lib/segment/custom-segment-tracking.ts | New early/opt-out Segment tracking helpers |
| app/scripts/lib/segment/custom-segment-tracking.test.ts | Tests for early tracking + opted-out tracking helper |
| app/scripts/lib/state-corruption/track-vault-corruption.ts | Route early tracking through new custom tracking module |
| app/scripts/lib/state-corruption/track-vault-corruption.test.ts | Update backup shape for AnalyticsController |
| app/scripts/lib/critical-error/track-critical-error.ts | Route early tracking through new custom tracking module |
| app/scripts/lib/critical-error/track-critical-error.test.ts | Update backup shape for AnalyticsController |
| app/scripts/lib/setup-initial-state-hooks.js | Use new custom segment tracking import |
| app/scripts/lib/sentry-get-state.ts | Derive participation/id from AnalyticsController + onboarding completion |
| app/scripts/lib/sentry-get-state.test.ts | Update tests for new state derivation rules |
| app/scripts/lib/sentry-make-transport.test.ts | Update persisted/backup Sentry snapshot expectations |
| app/scripts/constants/sentry-state.ts | Add AnalyticsController masks; update MetaMetricsController masks |
| app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts | Use analyticsId (instead of metaMetricsId) in handler typings/logic |
| app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts | Update test state for analyticsId |
| app/scripts/lib/createRPCMethodTrackingMiddleware.js | Gate tracking via AnalyticsController.optedIn |
| app/scripts/lib/createRPCMethodTrackingMiddleware.test.js | Add AnalyticsController messenger stubs + updated controller construction |
| app/scripts/background.js | Use controller.getState()-derived legacy id/consent for event emission gating |
| ui/store/actions.ts | Remove UI-generated actionId for metrics calls |
| ui/selectors/selectors.js | Map legacy getMetaMetricsId to analyticsId |
| ui/selectors/metametrics.js | Derive participation from onboarding completion + optedIn |
| ui/selectors/metametrics.test.ts | Add selector tests for new derived behavior |
| ui/contexts/metametrics.tsx | Update buffering + page tracking calls for new action signatures |
| ui/contexts/metametrics.test.tsx | Update provider tests for analyticsId/optedIn/completed onboarding |
| ui/pages/settings/privacy-tab/metametrics-item.tsx | Treat participateInMetaMetrics as tri-state; coerce to boolean for toggle |
| ui/pages/settings/privacy-tab/metametrics-item.test.tsx | Update tests for new state keys |
| ui/pages/settings/privacy-tab/data-collection-item.test.tsx | Update tests for new state keys |
| ui/pages/onboarding-flow/metametrics/metametrics.tsx | Handle tri-state participation when initializing UI |
| ui/pages/onboarding-flow/creation-successful/creation-successful.tsx | Simplify opt-in/out tracking call signature |
| ui/pages/onboarding-flow/create-password/create-password.test.tsx | Update tests to analyticsId + new consent fields |
| ui/pages/onboarding-flow/setup-passkey/setup-passkey.test.tsx | Update tests for new consent fields |
| ui/pages/onboarding-flow/onboarding-flow-switch/onboarding-flow-switch.test.tsx | Add routing case for incomplete metrics onboarding |
| ui/pages/home/home.container.js | Compute participateInMetaMetrics via selector |
| ui/pages/home/home.component.js | Make PropTypes accept tri-state/optional |
| ui/pages/asset/components/asset-page.tsx | Coerce metrics/marketing enabled flags to booleans for URL building |
| ui/hooks/ramps/useRamps/useRamps.ts | Coerce metrics enabled query param to boolean string |
| ui/hooks/ramps/useRamps/useRamps.test.tsx | Update mock state to analyticsId |
| ui/ducks/metamask/metamask.js | Map SET_PARTICIPATE_IN_METAMETRICS to completed/optedIn fields |
| ui/ducks/metamask/metamask.test.js | Add reducer tests for completed/optedIn mapping |
| ui/components/app/toast-master/selectors.ts | Update PNA25 gating to completed onboarding + optedIn |
| ui/components/app/metametrics-toggle/metametrics-toggle.test.tsx | Update tests for new consent fields |
| ui/components/ui/survey-toast/survey-toast.test.tsx | Update tests for analyticsId + consent fields |
| ui/components/multichain/token-list-item/stakeable-link.tsx | Coerce enabled flags to strict booleans |
| ui/components/multichain/menu-items/discover-menu-item.tsx | Coerce enabled flags to strict booleans |
| ui/components/multichain/global-menu-drawer/useGlobalMenuSections.tsx | Coerce enabled flags to strict booleans |
| ui/components/multichain/funding-method-modal/funding-method-modal.tsx | Coerce enabled flags to strict booleans |
| ui/components/app/wallet-overview/coin-overview.tsx | Coerce enabled flags to strict booleans |
| ui/components/app/assets/defi-list/cells/defi-empty-state.tsx | Coerce enabled flags to strict booleans |
| test/jest/console-baseline-unit.json | Update console baseline after controller refactor |
| test/integration/notifications&auth/notifications-toggle.test.tsx | Update integration state keys to analyticsId/optedIn |
| test/integration/notifications&auth/notifications-list.test.tsx | Update integration state keys to optedIn/completed onboarding |
| test/integration/notifications&auth/notifications-activation.test.tsx | Update integration state keys to optedIn/completed onboarding |
| test/integration/notifications&auth/data/notification-state.ts | Update mocked state metaMetricsId → analyticsId |
| test/integration/nfts/nfts.test.tsx | Update integration state keys for consent |
| test/integration/defi/defi-positions.test.tsx | Update integration state keys for analyticsId/consent |
| test/integration/data/onboarding-completion-route.json | Update fixture state to analyticsId/optedIn/completed onboarding |
| test/integration/data/integration-init-state.json | Update fixture state to optedIn/completed onboarding |
| test/integration/confirmations/transactions/contract-interaction.test.tsx | Update preloaded state keys |
| test/integration/confirmations/transactions/contract-deployment.test.tsx | Update preloaded state keys |
| test/integration/confirmations/signatures/personalSign.test.tsx | Update preloaded state keys |
| test/integration/confirmations/signatures/permit.test.tsx | Update preloaded state keys |
| test/data/mock-state.json | Update mock state keys to optedIn/completed onboarding |
| test/data/mock-send-state.json | Update mock state keys to optedIn/completed onboarding |
| test/e2e/tests/settings/state-logs.json | Update expected state-log schema keys |
| test/e2e/tests/metrics/wallet-imported.spec.ts | Remove fixture builder opt-in patch usage |
| test/e2e/tests/metrics/metametrics-persistence.spec.ts | Assert analyticsId persistence instead of metaMetricsId |
| test/e2e/tests/metrics/errors.spec.ts | Update Sentry assertions for new controller state locations |
| test/e2e/tests/bridge/swap-positive-cases.spec.ts | Improve event slicing logic robustness |
| test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json | Update snapshot schema + migration version |
| test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-background-state.json | Update snapshot schema for AnalyticsController |
| test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json | Update snapshot schema for AnalyticsController/eventQueue |
| test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json | Update snapshot schema for AnalyticsController |
| test/e2e/fixtures/onboarding-fixture.json | Update fixtures to migration 212 + AnalyticsController state |
| test/e2e/fixtures/default-fixture.json | Update fixtures to migration 212 + AnalyticsController state |
| test/e2e/fixtures/fixture-validation.ts | Ignore AnalyticsController.analyticsId variability |
| test/e2e/fixtures/fixture-builder-v2.ts | Support legacy metametrics patches by mapping into new controller state |
| test/e2e/dist/wallet-fixture-validation.spec.ts | Update dist wallet setup args for new consent behavior |
| test/e2e/dist/wallet-fixture-export.spec.ts | Update dist wallet setup args for new consent behavior |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "@metamask/analytics-controller": { | ||
| "packages": { | ||
| "@metamask/base-controller": true, | ||
| "@metamask/utils": true, | ||
| "lodash": true, | ||
| "uuid": true | ||
| } | ||
| }, |
There was a problem hiding this comment.
Policy changes are for safe package access.
| }, | ||
| { | ||
| isOptIn: !participateInMetaMetrics, // Force the event to be tracked even if participateInMetaMetrics is false | ||
| trackEvent({ |
There was a problem hiding this comment.
Can we use the trackSegmentEventWhileOptedOut just as you mentioned in the comment?
We wanna track this single particular event MetricsOptOut even when the participation is false.
There was a problem hiding this comment.
Hi @lwin-kyaw , with this new implementation, trackEvent calls #trackMetricsOptOutEvent under the hood here, which itself calls trackSegmentEventWhileOptedOut. This should support your use case.
seaona
left a comment
There was a problem hiding this comment.
e2e changes LGTM.
Would this be tackled by platform team? or do you want me to create a ticket for it for QA?
https://github.com/MetaMask/metamask-extension/pull/42885/changes#r3343851552
|
@seaona , it should be tackled by platform team as part of the next steps of AnalyticsController integration, this PR is just the first step. |
jiexi
left a comment
There was a problem hiding this comment.
Approved for Wallet Integration CO files

Description
This PR integrates AnalyticsController in Extension codebase.
It replaces this state:
metaMetricsIdparticipateInMetaMetricssegmentApiCallsby this state:
analyticsIdoptedIneventQueuecompletedMetaMetricsOnboardingSince these changes impact many areas of the codebase, we’ve decided to take a phased approach.
This PR migrates the persisted state and updates the UI compatibility selectors, as well as the background
getState()function, to derive the legacy values (metaMetricsId,participateInMetaMetrics) from the new state. This lets existing code continue using the legacy values while the source of truth moves toAnalyticsController.analyticsId,AnalyticsController.optedIn, andMetaMetricsController.completedMetaMetricsOnboarding.In future phases, we’ll progressively remove all remaining usages of the legacy values (
metaMetricsId,participateInMetaMetrics) throughout the codebase.The files that deserve more attention during PR review are the following:
flowchart TB subgraph Before["Before"] direction LR MMC1["MetaMetricsController"] --> SDK1["Segment SDK"] Early1["Early events"] --> SDK1 end subgraph After["After"] direction LR MMC2["MetaMetricsController"] --> AC["AnalyticsController"] AC --> SDK2["Segment SDK"] Early2["Early events"] --> SDK2 end Before ~~~ AfterChangelog
CHANGELOG entry: null
Related issues
Fixes: https://github.com/MetaMask/MetaMask-planning/issues/7193
Manual testing steps
Main test case
MetaMask Extension [Dev], except if you configured SEGMENT_WRITE_KEY env variable with your own Segment project), and filter events with your metaMetricsId/analyticsIda. track
b. identify
c. page
Edge cases
messageId(example: "signature-3947127447", and "signature-3947127447-success" or "signature-3947127447-failure"), while with this PR,messageIdhave a new uuidv4 format and events from a same fragment don't have similarmessageId(example: "9e3a83d0-ce13-47d2-a2f4-08a6d9adc29d" and "d2368a25-01c5-4b3c-af55-7d5f54eb58a6")timestampandsentAtScreenshots/Recordings
NA
Pre-merge author checklist
Pre-merge reviewer checklist
Note
High Risk
Central refactor of consent, IDs, queuing, and anonymous event handling across background, Segment, and data-deletion paths; messageId and offline-queue behavior differ from main and can affect metrics parity or stuck retries.
Overview
This PR routes MetaMetrics track / identify / page through
AnalyticsControllerand a new Segmentplatform-adapter, instead ofMetaMetricsControllercalling Segment directly.State & consent: Persisted
metaMetricsId,participateInMetaMetrics, andsegmentApiCallsare removed fromMetaMetricsControllerin favor ofAnalyticsController(analyticsId,optedIn,eventQueue) pluscompletedMetaMetricsOnboarding. Opt-in/out now callsAnalyticsController:optIn/optOut; IDs come fromgetMetaMetricsId()→ analytics state. Sentry snapshots addAnalyticsControllerand drop the old MMC consent fields.Behavior changes: Anonymous / sensitive events use a
properties.anonymousmarker; the adapter swaps to the shared anonymous ID and renames select signature/transaction events. Direct Segment logic (payload building,segmentApiCallsMV3 replay, deterministicmessageId,generateMetaMetricsId) is removed from MMC. Early and opt-out paths usecustom-segment-trackingreadingAnalyticsControllerfrom backup state. RPC middleware, data deletion, and background helpers read consent/ID viacontroller.getState()/analyticsController.state.Tests: Large
metametrics-controllertest updates; newplatform-adapterunit tests.Reviewed by Cursor Bugbot for commit 5d73b11. Bugbot is set up for automated code reviews on this repo. Configure here.