Skip to content

Conversation

@mikesposito
Copy link
Member

@mikesposito mikesposito commented Nov 19, 2025

Explanation

This PR adds ProfileMetricsController to @metamask/profile-metrics-controller.

The new controller manages a queue of addresses that need to be sent to ProfileMetricsService

The queue is updated in the following scenarios:

  • When the wallet is being unlocked AND firstSyncCompleted is false (i.e., first sync has not been completed yet)
  • When a new account is added
  • When an account is removed

The queue is processed at regular intervals (every 10 seconds by default). At each polling interval, if there are items in the queue, the controller will attempt to call ProfileMetricsService through the messenger to sync addresses in batches, grouped by their entropy source. Accounts with no entropy source will be batched together.
The queue is persisted to storage, so that if the app is closed and reopened, the sync process can continue from where it left off. If one of the batch syncs fails, the same batch will be retried in the next interval.

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed, highlighting breaking changes as necessary
  • I've prepared draft pull requests for clients and consumer packages to resolve any breaking changes

Note

Adds ProfileMetricsController that queues accounts and periodically submits grouped metrics via messenger, with persistence, mutexing, and comprehensive tests.

  • New Controller: src/ProfileMetricsController.ts
    • Polling via StaticIntervalPollingController (default 10s) to call ProfileMetricsService:submitMetrics.
    • Maintains persistent syncQueue grouped by entropy source (null for none); guarded by async-mutex.
    • Subscribes to KeyringController:unlock/lock and AccountsController:accountAdded/accountRemoved to enqueue/dequeue; initial enqueue on unlock if not completed.
    • Batches submissions per entropy source; logs and skips failed batches while continuing others.
    • Exposes state with metadata (initialEnqueueCompleted, syncQueue).
  • Tests: src/ProfileMetricsController.test.ts covering subscriptions, polling behavior, batching, failure handling, and state metadata.
  • Exports: Update src/index.ts to export controller types and helpers.
  • Project Setup:
    • Add dependencies: @metamask/accounts-controller, @metamask/keyring-controller, @metamask/polling-controller, async-mutex (and dev @metamask/keyring-internal-api).
    • Update TS references in tsconfig.json and tsconfig.build.json.
    • Update CHANGELOG.md entry for initial release.

Written by Cursor Bugbot for commit bc80feb. This will update automatically on new commits. Configure here.

@mikesposito mikesposito force-pushed the mikesposito/user-profile-controller branch from 522b8f7 to 747f8f7 Compare November 19, 2025 22:54
@mikesposito mikesposito marked this pull request as ready for review November 26, 2025 12:36
@mikesposito mikesposito requested a review from a team as a code owner November 26, 2025 12:36
@mikesposito
Copy link
Member Author

@metamaskbot publish-preview

@github-actions
Copy link
Contributor

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/account-tree-controller": "4.0.0-preview-8d461ad5",
  "@metamask-previews/accounts-controller": "35.0.0-preview-8d461ad5",
  "@metamask-previews/address-book-controller": "7.0.1-preview-8d461ad5",
  "@metamask-previews/analytics-controller": "0.0.0-preview-8d461ad5",
  "@metamask-previews/announcement-controller": "8.0.0-preview-8d461ad5",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-8d461ad5",
  "@metamask-previews/approval-controller": "8.0.0-preview-8d461ad5",
  "@metamask-previews/assets-controllers": "91.0.0-preview-8d461ad5",
  "@metamask-previews/base-controller": "9.0.0-preview-8d461ad5",
  "@metamask-previews/bridge-controller": "63.2.0-preview-8d461ad5",
  "@metamask-previews/bridge-status-controller": "63.1.0-preview-8d461ad5",
  "@metamask-previews/build-utils": "3.0.4-preview-8d461ad5",
  "@metamask-previews/chain-agnostic-permission": "1.2.2-preview-8d461ad5",
  "@metamask-previews/claims-controller": "0.2.0-preview-8d461ad5",
  "@metamask-previews/composable-controller": "12.0.0-preview-8d461ad5",
  "@metamask-previews/controller-utils": "11.16.0-preview-8d461ad5",
  "@metamask-previews/core-backend": "5.0.0-preview-8d461ad5",
  "@metamask-previews/delegation-controller": "2.0.0-preview-8d461ad5",
  "@metamask-previews/earn-controller": "11.0.0-preview-8d461ad5",
  "@metamask-previews/eip-5792-middleware": "2.0.0-preview-8d461ad5",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-8d461ad5",
  "@metamask-previews/eip1193-permission-middleware": "1.0.2-preview-8d461ad5",
  "@metamask-previews/ens-controller": "19.0.0-preview-8d461ad5",
  "@metamask-previews/error-reporting-service": "3.0.0-preview-8d461ad5",
  "@metamask-previews/eth-block-tracker": "15.0.0-preview-8d461ad5",
  "@metamask-previews/eth-json-rpc-middleware": "22.0.0-preview-8d461ad5",
  "@metamask-previews/eth-json-rpc-provider": "6.0.0-preview-8d461ad5",
  "@metamask-previews/foundryup": "1.0.1-preview-8d461ad5",
  "@metamask-previews/gas-fee-controller": "26.0.0-preview-8d461ad5",
  "@metamask-previews/gator-permissions-controller": "0.6.0-preview-8d461ad5",
  "@metamask-previews/json-rpc-engine": "10.2.0-preview-8d461ad5",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-8d461ad5",
  "@metamask-previews/keyring-controller": "25.0.0-preview-8d461ad5",
  "@metamask-previews/logging-controller": "7.0.1-preview-8d461ad5",
  "@metamask-previews/message-manager": "14.1.0-preview-8d461ad5",
  "@metamask-previews/messenger": "0.3.0-preview-8d461ad5",
  "@metamask-previews/multichain-account-service": "4.0.0-preview-8d461ad5",
  "@metamask-previews/multichain-api-middleware": "1.2.4-preview-8d461ad5",
  "@metamask-previews/multichain-network-controller": "3.0.0-preview-8d461ad5",
  "@metamask-previews/multichain-transactions-controller": "7.0.0-preview-8d461ad5",
  "@metamask-previews/name-controller": "9.0.0-preview-8d461ad5",
  "@metamask-previews/network-controller": "26.0.0-preview-8d461ad5",
  "@metamask-previews/network-enablement-controller": "4.0.0-preview-8d461ad5",
  "@metamask-previews/notification-services-controller": "21.0.0-preview-8d461ad5",
  "@metamask-previews/permission-controller": "12.1.1-preview-8d461ad5",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-8d461ad5",
  "@metamask-previews/phishing-controller": "16.1.0-preview-8d461ad5",
  "@metamask-previews/polling-controller": "16.0.0-preview-8d461ad5",
  "@metamask-previews/preferences-controller": "22.0.0-preview-8d461ad5",
  "@metamask-previews/profile-metrics-controller": "0.0.0-preview-8d461ad5",
  "@metamask-previews/profile-sync-controller": "27.0.0-preview-8d461ad5",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-8d461ad5",
  "@metamask-previews/remote-feature-flag-controller": "2.0.1-preview-8d461ad5",
  "@metamask-previews/sample-controllers": "4.0.0-preview-8d461ad5",
  "@metamask-previews/seedless-onboarding-controller": "7.0.0-preview-8d461ad5",
  "@metamask-previews/selected-network-controller": "26.0.0-preview-8d461ad5",
  "@metamask-previews/shield-controller": "3.1.0-preview-8d461ad5",
  "@metamask-previews/signature-controller": "37.0.0-preview-8d461ad5",
  "@metamask-previews/subscription-controller": "5.1.0-preview-8d461ad5",
  "@metamask-previews/token-search-discovery-controller": "4.0.0-preview-8d461ad5",
  "@metamask-previews/transaction-controller": "62.3.0-preview-8d461ad5",
  "@metamask-previews/transaction-pay-controller": "10.1.0-preview-8d461ad5",
  "@metamask-previews/user-operation-controller": "41.0.0-preview-8d461ad5"
}

mikesposito and others added 2 commits November 27, 2025 14:28
danroc
danroc previously approved these changes Nov 27, 2025
cryptodev-2s
cryptodev-2s previously approved these changes Nov 27, 2025
@mikesposito mikesposito dismissed stale reviews from cryptodev-2s and danroc via f4611d4 November 28, 2025 11:36
@mikesposito
Copy link
Member Author

@metamaskbot publish-preview

@github-actions
Copy link
Contributor

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/account-tree-controller": "4.0.0-preview-bc80feb8",
  "@metamask-previews/accounts-controller": "35.0.0-preview-bc80feb8",
  "@metamask-previews/address-book-controller": "7.0.1-preview-bc80feb8",
  "@metamask-previews/analytics-controller": "0.0.0-preview-bc80feb8",
  "@metamask-previews/announcement-controller": "8.0.0-preview-bc80feb8",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-bc80feb8",
  "@metamask-previews/approval-controller": "8.0.0-preview-bc80feb8",
  "@metamask-previews/assets-controllers": "91.0.0-preview-bc80feb8",
  "@metamask-previews/base-controller": "9.0.0-preview-bc80feb8",
  "@metamask-previews/bridge-controller": "63.2.0-preview-bc80feb8",
  "@metamask-previews/bridge-status-controller": "63.1.0-preview-bc80feb8",
  "@metamask-previews/build-utils": "3.0.4-preview-bc80feb8",
  "@metamask-previews/chain-agnostic-permission": "1.2.2-preview-bc80feb8",
  "@metamask-previews/claims-controller": "0.2.0-preview-bc80feb8",
  "@metamask-previews/composable-controller": "12.0.0-preview-bc80feb8",
  "@metamask-previews/controller-utils": "11.16.0-preview-bc80feb8",
  "@metamask-previews/core-backend": "5.0.0-preview-bc80feb8",
  "@metamask-previews/delegation-controller": "2.0.0-preview-bc80feb8",
  "@metamask-previews/earn-controller": "11.0.0-preview-bc80feb8",
  "@metamask-previews/eip-5792-middleware": "2.0.0-preview-bc80feb8",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-bc80feb8",
  "@metamask-previews/eip1193-permission-middleware": "1.0.2-preview-bc80feb8",
  "@metamask-previews/ens-controller": "19.0.0-preview-bc80feb8",
  "@metamask-previews/error-reporting-service": "3.0.0-preview-bc80feb8",
  "@metamask-previews/eth-block-tracker": "15.0.0-preview-bc80feb8",
  "@metamask-previews/eth-json-rpc-middleware": "22.0.0-preview-bc80feb8",
  "@metamask-previews/eth-json-rpc-provider": "6.0.0-preview-bc80feb8",
  "@metamask-previews/foundryup": "1.0.1-preview-bc80feb8",
  "@metamask-previews/gas-fee-controller": "26.0.0-preview-bc80feb8",
  "@metamask-previews/gator-permissions-controller": "0.6.0-preview-bc80feb8",
  "@metamask-previews/json-rpc-engine": "10.2.0-preview-bc80feb8",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-bc80feb8",
  "@metamask-previews/keyring-controller": "25.0.0-preview-bc80feb8",
  "@metamask-previews/logging-controller": "7.0.1-preview-bc80feb8",
  "@metamask-previews/message-manager": "14.1.0-preview-bc80feb8",
  "@metamask-previews/messenger": "0.3.0-preview-bc80feb8",
  "@metamask-previews/multichain-account-service": "4.0.0-preview-bc80feb8",
  "@metamask-previews/multichain-api-middleware": "1.2.4-preview-bc80feb8",
  "@metamask-previews/multichain-network-controller": "3.0.0-preview-bc80feb8",
  "@metamask-previews/multichain-transactions-controller": "7.0.0-preview-bc80feb8",
  "@metamask-previews/name-controller": "9.0.0-preview-bc80feb8",
  "@metamask-previews/network-controller": "26.0.0-preview-bc80feb8",
  "@metamask-previews/network-enablement-controller": "4.0.0-preview-bc80feb8",
  "@metamask-previews/notification-services-controller": "21.0.0-preview-bc80feb8",
  "@metamask-previews/permission-controller": "12.1.1-preview-bc80feb8",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-bc80feb8",
  "@metamask-previews/phishing-controller": "16.1.0-preview-bc80feb8",
  "@metamask-previews/polling-controller": "16.0.0-preview-bc80feb8",
  "@metamask-previews/preferences-controller": "22.0.0-preview-bc80feb8",
  "@metamask-previews/profile-metrics-controller": "0.0.0-preview-bc80feb8",
  "@metamask-previews/profile-sync-controller": "27.0.0-preview-bc80feb8",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-bc80feb8",
  "@metamask-previews/remote-feature-flag-controller": "2.0.1-preview-bc80feb8",
  "@metamask-previews/sample-controllers": "4.0.0-preview-bc80feb8",
  "@metamask-previews/seedless-onboarding-controller": "7.0.0-preview-bc80feb8",
  "@metamask-previews/selected-network-controller": "26.0.0-preview-bc80feb8",
  "@metamask-previews/shield-controller": "3.1.0-preview-bc80feb8",
  "@metamask-previews/signature-controller": "37.0.0-preview-bc80feb8",
  "@metamask-previews/subscription-controller": "5.1.0-preview-bc80feb8",
  "@metamask-previews/token-search-discovery-controller": "4.0.0-preview-bc80feb8",
  "@metamask-previews/transaction-controller": "62.3.0-preview-bc80feb8",
  "@metamask-previews/transaction-pay-controller": "10.1.0-preview-bc80feb8",
  "@metamask-previews/user-operation-controller": "41.0.0-preview-bc80feb8"
}

state.syncQueue[key] = [];
}
state.syncQueue[key].push(...newGroupedAccounts[key]);
}
Copy link

Choose a reason for hiding this comment

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

Bug: Duplicate accounts added to queue during initial sync

The #queueFirstSyncIfNeeded method appends all accounts from listAccounts to syncQueue without checking if they already exist. If AccountsController:accountAdded events fire before KeyringController:unlock (possible during wallet setup), accounts added by #addAccountToQueue will be duplicated when #queueFirstSyncIfNeeded runs on unlock. The code checks if syncQueue[key] exists but then blindly pushes accounts rather than deduplicating, causing the same accounts to be submitted multiple times to ProfileMetricsService.

Fix in Cursor Fix in Web

Copy link
Contributor

@cryptodev-2s cryptodev-2s left a comment

Choose a reason for hiding this comment

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

LGTM!

@mikesposito mikesposito added this pull request to the merge queue Nov 28, 2025
Merged via the queue into main with commit 8f42d26 Nov 28, 2025
277 checks passed
@mikesposito mikesposito deleted the mikesposito/user-profile-controller-queue branch November 28, 2025 13:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants