@metamask-previews/profile-metrics-controller 4.0.0-preview-18dd0df9c → 4.0.0-preview-bd7b53f49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/dist/ProfileMetricsController.cjs +8 -3
- package/dist/ProfileMetricsController.cjs.map +1 -1
- package/dist/ProfileMetricsController.d.cts.map +1 -1
- package/dist/ProfileMetricsController.d.mts.map +1 -1
- package/dist/ProfileMetricsController.mjs +8 -3
- package/dist/ProfileMetricsController.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
12
12
|
- Bump `@metamask/accounts-controller` from `^39.0.2` to `^39.0.3` ([#9231](https://github.com/MetaMask/core/pull/9231))
|
|
13
13
|
- Bump `@metamask/transaction-controller` from `^68.1.1` to `^68.2.0` ([#9253](https://github.com/MetaMask/core/pull/9253))
|
|
14
14
|
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Skip proof-of-ownership signing for accounts with no entropy source ([#9286](https://github.com/MetaMask/core/pull/9286))
|
|
18
|
+
- They are still submitted to the metrics endpoint, just without a `proof` field.
|
|
19
|
+
|
|
15
20
|
## [4.0.0]
|
|
16
21
|
|
|
17
22
|
### Added
|
|
@@ -166,7 +166,10 @@ class ProfileMetricsController extends (0, polling_controller_1.StaticIntervalPo
|
|
|
166
166
|
const fullAccountsByAddress = __classPrivateFieldGet(this, _ProfileMetricsController_instances, "m", _ProfileMetricsController_getFullAccountsByAddress).call(this);
|
|
167
167
|
for (const [entropySourceId, accounts] of Object.entries(this.state.syncQueue)) {
|
|
168
168
|
const normalizedEntropySourceId = entropySourceId === 'null' ? null : entropySourceId;
|
|
169
|
-
|
|
169
|
+
// Skip proof-of-ownership for accounts without an entropy source
|
|
170
|
+
const accountsWithProofs = normalizedEntropySourceId === null
|
|
171
|
+
? accounts
|
|
172
|
+
: await __classPrivateFieldGet(this, _ProfileMetricsController_instances, "m", _ProfileMetricsController_attachProofs).call(this, accounts, fullAccountsByAddress, normalizedEntropySourceId);
|
|
170
173
|
try {
|
|
171
174
|
await this.messenger.call('ProfileMetricsService:submitMetrics', {
|
|
172
175
|
metametricsId: __classPrivateFieldGet(this, _ProfileMetricsController_getMetaMetricsId, "f").call(this),
|
|
@@ -198,7 +201,9 @@ _ProfileMetricsController_mutex = new WeakMap(), _ProfileMetricsController_asser
|
|
|
198
201
|
*
|
|
199
202
|
* @param accounts - The queued accounts for a single batch.
|
|
200
203
|
* @param fullAccountsByAddress - Live `InternalAccount` lookup keyed by address.
|
|
201
|
-
* @param entropySourceId - The entropy source ID for this batch.
|
|
204
|
+
* @param entropySourceId - The entropy source ID for this batch. Callers
|
|
205
|
+
* are expected to short-circuit before invoking this method when the
|
|
206
|
+
* batch has no entropy source; see `_executePoll` for why.
|
|
202
207
|
* @returns The accounts with `proof` populated where signing succeeded.
|
|
203
208
|
*/
|
|
204
209
|
async function _ProfileMetricsController_attachProofs(accounts, fullAccountsByAddress, entropySourceId) {
|
|
@@ -241,7 +246,7 @@ async function _ProfileMetricsController_attachProofs(accounts, fullAccountsByAd
|
|
|
241
246
|
});
|
|
242
247
|
}
|
|
243
248
|
catch (error) {
|
|
244
|
-
console.error(`Failed to fetch proof-of-ownership nonces for entropy source ID ${entropySourceId
|
|
249
|
+
console.error(`Failed to fetch proof-of-ownership nonces for entropy source ID ${entropySourceId}:`, error);
|
|
245
250
|
}
|
|
246
251
|
return await Promise.all(accounts.map(async (queued) => {
|
|
247
252
|
const candidate = candidates.get(queued.address);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProfileMetricsController.cjs","sourceRoot":"","sources":["../src/ProfileMetricsController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AccountsControllerAccountAddedEvent,\n AccountsControllerAccountRemovedEvent,\n AccountsControllerGetStateAction,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport type {\n KeyringControllerLockEvent,\n KeyringControllerUnlockEvent,\n} from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { Messenger } from '@metamask/messenger';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport { TransactionControllerTransactionSubmittedEvent } from '@metamask/transaction-controller';\nimport { Duration, inMilliseconds, parseCaipChainId } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\n\nimport type { ProfileMetricsControllerMethodActions } from './ProfileMetricsController-method-action-types';\nimport type {\n AccountOwnershipProof,\n AccountWithScopes,\n} from './ProfileMetricsService';\nimport type { ProfileMetricsServiceMethodActions } from './ProfileMetricsService-method-action-types';\nimport type { ProofOfOwnershipServiceMethodActions } from './ProofOfOwnershipService-method-action-types';\nimport {\n canonicalizeAddress,\n ProofUnsupportedNamespaceError,\n} from './utils/canonicalize';\n\n/**\n * The name of the {@link ProfileMetricsController}, used to namespace the\n * controller's actions and events and to namespace the controller's state data\n * when composed with other controllers.\n */\nexport const controllerName = 'ProfileMetricsController';\n\n/**\n * The default delay duration before data is sent for the first time.\n */\nexport const DEFAULT_INITIAL_DELAY_DURATION = inMilliseconds(\n 1,\n Duration.Minute,\n);\n\n/**\n * Describes the shape of the state object for {@link ProfileMetricsController}.\n */\nexport type ProfileMetricsControllerState = {\n /**\n * Whether existing accounts have been added\n * to the queue.\n */\n initialEnqueueCompleted: boolean;\n /**\n * The queue of accounts to be synced.\n * Each key is an entropy source ID, and each value is an array of account\n * addresses associated with that entropy source. Accounts with no entropy\n * source ID are grouped under the key \"null\".\n */\n syncQueue: Record<string, AccountWithScopes[]>;\n /**\n * The timestamp when the first data sending can be attempted.\n */\n initialDelayEndTimestamp?: number;\n /**\n * Whether previously-synced accounts have been re-enqueued so their\n * proofs of ownership are submitted alongside everything else. Set on\n * the first unlock after upgrading to a version that signs proofs of\n * ownership; fresh installs flip this on their initial sync since the\n * first poll already attaches proofs.\n */\n proofBackfillEnqueued: boolean;\n};\n\n/**\n * The metadata for each property in {@link ProfileMetricsControllerState}.\n */\nconst profileMetricsControllerMetadata = {\n initialEnqueueCompleted: {\n persist: true,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: false,\n },\n syncQueue: {\n persist: true,\n includeInDebugSnapshot: false,\n includeInStateLogs: true,\n usedInUi: false,\n },\n initialDelayEndTimestamp: {\n persist: true,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: false,\n },\n proofBackfillEnqueued: {\n persist: true,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: false,\n },\n} satisfies StateMetadata<ProfileMetricsControllerState>;\n\n/**\n * Constructs the default {@link ProfileMetricsController} state. This allows\n * consumers to provide a partial state object when initializing the controller\n * and also helps in constructing complete state objects for this controller in\n * tests.\n *\n * @returns The default {@link ProfileMetricsController} state.\n */\nexport function getDefaultProfileMetricsControllerState(): ProfileMetricsControllerState {\n return {\n initialEnqueueCompleted: false,\n syncQueue: {},\n proofBackfillEnqueued: false,\n };\n}\n\nconst MESSENGER_EXPOSED_METHODS = ['skipInitialDelay'] as const;\n\n/**\n * Retrieves the state of the {@link ProfileMetricsController}.\n */\nexport type ProfileMetricsControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n ProfileMetricsControllerState\n>;\n\n/**\n * Actions that {@link ProfileMetricsControllerMessenger} exposes to other consumers.\n */\nexport type ProfileMetricsControllerActions =\n | ProfileMetricsControllerGetStateAction\n | ProfileMetricsControllerMethodActions;\n\n/**\n * Actions from other messengers that {@link ProfileMetricsControllerMessenger} calls.\n */\ntype AllowedActions =\n | ProfileMetricsServiceMethodActions\n | ProofOfOwnershipServiceMethodActions\n | AccountsControllerGetStateAction;\n\n/**\n * Published when the state of {@link ProfileMetricsController} changes.\n */\nexport type ProfileMetricsControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n ProfileMetricsControllerState\n >;\n\n/**\n * Events that {@link ProfileMetricsControllerMessenger} exposes to other consumers.\n */\nexport type ProfileMetricsControllerEvents =\n ProfileMetricsControllerStateChangeEvent;\n\n/**\n * Events from other messengers that {@link ProfileMetricsControllerMessenger} subscribes\n * to.\n */\ntype AllowedEvents =\n | KeyringControllerUnlockEvent\n | KeyringControllerLockEvent\n | AccountsControllerAccountAddedEvent\n | AccountsControllerAccountRemovedEvent\n | TransactionControllerTransactionSubmittedEvent;\n\n/**\n * The messenger restricted to actions and events accessed by\n * {@link ProfileMetricsController}.\n */\nexport type ProfileMetricsControllerMessenger = Messenger<\n typeof controllerName,\n ProfileMetricsControllerActions | AllowedActions,\n ProfileMetricsControllerEvents | AllowedEvents\n>;\n\n/**\n * Manages user profile metrics.\n *\n * For users who opt-in to metrics, this controller ensures we have metrics about their user\n * profile (metrics ID and accounts).\n */\nexport class ProfileMetricsController extends StaticIntervalPollingController()<\n typeof controllerName,\n ProfileMetricsControllerState,\n ProfileMetricsControllerMessenger\n> {\n readonly #mutex = new Mutex();\n\n readonly #assertUserOptedIn: () => boolean;\n\n readonly #getMetaMetricsId: () => string;\n\n readonly #initialDelayDuration: number;\n\n /**\n * Constructs a new {@link ProfileMetricsController}.\n *\n * @param args - The constructor arguments.\n * @param args.messenger - The messenger suited for this controller.\n * @param args.state - The desired state with which to initialize this\n * controller. Missing properties will be filled in with defaults.\n * @param args.assertUserOptedIn - A function that asserts whether the user has\n * opted in to user profile features. If the user has not opted in, sync\n * operations will be no-ops.\n * @param args.getMetaMetricsId - A function that returns the MetaMetrics ID\n * of the user.\n * @param args.interval - The interval, in milliseconds, at which the controller will\n * attempt to send user profile data. Defaults to 10 seconds.\n * @param args.initialDelayDuration - The delay duration before data is sent\n * for the first time, in milliseconds. Defaults to 10 minutes.\n */\n constructor({\n messenger,\n state,\n assertUserOptedIn,\n getMetaMetricsId,\n interval = 10 * 1000,\n initialDelayDuration = DEFAULT_INITIAL_DELAY_DURATION,\n }: {\n messenger: ProfileMetricsControllerMessenger;\n state?: Partial<ProfileMetricsControllerState>;\n interval?: number;\n assertUserOptedIn: () => boolean;\n getMetaMetricsId: () => string;\n initialDelayDuration?: number;\n }) {\n super({\n messenger,\n metadata: profileMetricsControllerMetadata,\n name: controllerName,\n state: {\n ...getDefaultProfileMetricsControllerState(),\n ...state,\n },\n });\n\n this.#assertUserOptedIn = assertUserOptedIn;\n this.#getMetaMetricsId = getMetaMetricsId;\n this.#initialDelayDuration = initialDelayDuration;\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n\n this.messenger.subscribe('KeyringController:unlock', () => {\n if (this.#assertUserOptedIn()) {\n // If the user has already opted in at the start of the session,\n // it must have opted in during onboarding, or during a previous session.\n this.skipInitialDelay();\n }\n this.#enqueueAccountsIfNeeded().catch(\n this.messenger.captureException ?? console.error,\n );\n this.startPolling(null);\n });\n\n this.messenger.subscribe('KeyringController:lock', () =>\n this.stopAllPolling(),\n );\n\n this.messenger.subscribe('TransactionController:transactionSubmitted', () =>\n this.skipInitialDelay(),\n );\n\n this.messenger.subscribe('AccountsController:accountAdded', (account) => {\n this.#addAccountToQueue(account).catch(console.error);\n });\n\n this.messenger.subscribe('AccountsController:accountRemoved', (account) => {\n this.#removeAccountFromQueue(account).catch(console.error);\n });\n\n this.setIntervalLength(interval);\n }\n\n /**\n * Skip the initial delay period by setting the end timestamp to the current time.\n * Metrics will be sent on the next poll.\n */\n skipInitialDelay(): void {\n this.update((state) => {\n state.initialDelayEndTimestamp = Date.now();\n });\n }\n\n /**\n * Execute a single poll to sync user profile data.\n *\n * The queued accounts are sent to the ProfileMetricsService, each with\n * a proof of ownership when one can be produced (see {@link #attachProofs}),\n * and the sync queue is cleared. This operation is mutexed to prevent\n * concurrent executions.\n *\n * @returns A promise that resolves when the poll is complete.\n */\n async _executePoll(): Promise<void> {\n await this.#mutex.runExclusive(async () => {\n if (!this.#assertUserOptedIn()) {\n return;\n }\n this.#setInitialDelayEndTimestampIfNull();\n if (!this.#isInitialDelayComplete()) {\n return;\n }\n const fullAccountsByAddress = this.#getFullAccountsByAddress();\n for (const [entropySourceId, accounts] of Object.entries(\n this.state.syncQueue,\n )) {\n const normalizedEntropySourceId =\n entropySourceId === 'null' ? null : entropySourceId;\n const accountsWithProofs = await this.#attachProofs(\n accounts,\n fullAccountsByAddress,\n normalizedEntropySourceId,\n );\n try {\n await this.messenger.call('ProfileMetricsService:submitMetrics', {\n metametricsId: this.#getMetaMetricsId(),\n entropySourceId: normalizedEntropySourceId,\n accounts: accountsWithProofs,\n });\n this.update((state) => {\n delete state.syncQueue[entropySourceId];\n });\n } catch (error) {\n // We want to log the error but continue processing other batches.\n console.error(\n `Failed to submit profile metrics for entropy source ID ${entropySourceId}:`,\n error,\n );\n }\n }\n });\n }\n\n /**\n * Attach a proof of ownership to each account in a single entropy-source\n * batch when possible, canonicalizing the address along the way.\n *\n * Per-account failures (unknown namespace, snap missing the\n * `signProofOfOwnership` method, snap rejection) and whole-batch nonce\n * failures are caught and downgraded to \"submit without a proof\" so the\n * batch still goes through and the proof is retried on the next poll.\n *\n * @param accounts - The queued accounts for a single batch.\n * @param fullAccountsByAddress - Live `InternalAccount` lookup keyed by address.\n * @param entropySourceId - The entropy source ID for this batch.\n * @returns The accounts with `proof` populated where signing succeeded.\n */\n async #attachProofs(\n accounts: AccountWithScopes[],\n fullAccountsByAddress: Map<string, InternalAccount>,\n entropySourceId: string | null,\n ): Promise<AccountWithScopes[]> {\n const candidates = new Map<\n string,\n { account: InternalAccount; canonicalAddress: string }\n >();\n const identifiers = new Set<string>();\n for (const queued of accounts) {\n const fullAccount = fullAccountsByAddress.get(queued.address);\n if (!fullAccount) {\n continue;\n }\n try {\n const [scope] = fullAccount.scopes;\n if (!scope) {\n throw new Error(`Scope not found for account ${fullAccount.id}`);\n }\n const { namespace } = parseCaipChainId(scope);\n const canonicalAddress = canonicalizeAddress(\n fullAccount.address,\n namespace,\n );\n candidates.set(queued.address, {\n account: fullAccount,\n canonicalAddress,\n });\n identifiers.add(canonicalAddress);\n } catch (error) {\n // Unsupported namespaces are an expected pass-through; anything\n // else is logged so a new namespace doesn't go unnoticed.\n if (!(error instanceof ProofUnsupportedNamespaceError)) {\n console.error(`Skipping proof for account ${fullAccount.id}:`, error);\n }\n }\n }\n\n if (candidates.size === 0) {\n return accounts;\n }\n\n let nonces: Record<string, string> = {};\n try {\n nonces = await this.messenger.call('ProfileMetricsService:fetchNonces', {\n identifiers: [...identifiers],\n entropySourceId,\n });\n } catch (error) {\n console.error(\n `Failed to fetch proof-of-ownership nonces for entropy source ID ${entropySourceId ?? 'null'}:`,\n error,\n );\n }\n\n return await Promise.all(\n accounts.map(async (queued): Promise<AccountWithScopes> => {\n const candidate = candidates.get(queued.address);\n if (!candidate) {\n return queued;\n }\n const nonce = nonces[candidate.canonicalAddress];\n if (!nonce) {\n return { ...queued, address: candidate.canonicalAddress };\n }\n let proof: AccountOwnershipProof;\n try {\n proof = await this.messenger.call('ProofOfOwnershipService:sign', {\n account: candidate.account,\n nonce,\n });\n } catch (error) {\n console.error(\n `Failed to sign proof of ownership for account ${candidate.account.id}:`,\n error,\n );\n return { ...queued, address: candidate.canonicalAddress };\n }\n return {\n address: candidate.canonicalAddress,\n scopes: queued.scopes,\n proof,\n };\n }),\n );\n }\n\n /**\n * Snapshot the live `InternalAccount` map keyed by address for the\n * current poll.\n *\n * @returns A map of address → `InternalAccount`.\n */\n #getFullAccountsByAddress(): Map<string, InternalAccount> {\n const byAddress = new Map<string, InternalAccount>();\n const accountsState = this.messenger.call('AccountsController:getState');\n for (const account of Object.values(\n accountsState.internalAccounts.accounts,\n )) {\n byAddress.set(account.address, account);\n }\n return byAddress;\n }\n\n /**\n * Enqueue all currently-known accounts onto the sync queue if needed.\n * Single entry point covering both the fresh-install first sync and\n * the one-time proof-of-ownership backfill for users upgrading.\n *\n * Bails for opted-out users (the poll wouldn't drain the queue\n * anyway), and bails once both bootstrap steps have already run.\n * Otherwise enqueues all known accounts and flips both flags so this\n * becomes a permanent no-op for the lifetime of the install.\n */\n async #enqueueAccountsIfNeeded(): Promise<void> {\n await this.#mutex.runExclusive(async () => {\n if (!this.#assertUserOptedIn()) {\n return;\n }\n if (\n this.state.initialEnqueueCompleted &&\n this.state.proofBackfillEnqueued\n ) {\n return;\n }\n const groupedAccounts = groupAccountsByEntropySourceId(\n Object.values(\n this.messenger.call('AccountsController:getState').internalAccounts\n .accounts,\n ),\n );\n this.update((state) => {\n // Replace the queue rather than append. `AccountsController` is\n // the source of truth and the queue is otherwise kept in sync\n // with it via the `accountAdded` / `accountRemoved` subscriptions,\n // so assigning here avoids duplicating entries that survived from\n // a prior session or were pushed earlier in this same unlock\n // cycle. Duplicates would matter because nonces are single-use:\n // letting one through causes `#attachProofs` to sign and submit\n // twice with the same nonce.\n state.syncQueue = groupedAccounts;\n state.initialEnqueueCompleted = true;\n state.proofBackfillEnqueued = true;\n });\n });\n }\n\n /**\n * Set the initial delay end timestamp if it is not already set.\n */\n #setInitialDelayEndTimestampIfNull(): void {\n this.update((state) => {\n state.initialDelayEndTimestamp ??=\n Date.now() + this.#initialDelayDuration;\n });\n }\n\n /**\n * Check if the initial delay end timestamp is in the past.\n *\n * @returns True if the initial delay period has completed, false otherwise.\n */\n #isInitialDelayComplete(): boolean {\n return (\n this.state.initialDelayEndTimestamp !== undefined &&\n Date.now() >= this.state.initialDelayEndTimestamp\n );\n }\n\n /**\n * Queue the given account to be synced at the next poll.\n *\n * @param account - The account to sync.\n */\n async #addAccountToQueue(account: InternalAccount): Promise<void> {\n await this.#mutex.runExclusive(async () => {\n this.update((state) => {\n const entropySourceId = getAccountEntropySourceId(account) ?? 'null';\n if (!state.syncQueue[entropySourceId]) {\n state.syncQueue[entropySourceId] = [];\n }\n state.syncQueue[entropySourceId].push({\n address: account.address,\n scopes: account.scopes,\n });\n });\n });\n }\n\n /**\n * Remove the given account from the sync queue.\n *\n * @param account - The account address to remove.\n */\n async #removeAccountFromQueue(account: string): Promise<void> {\n await this.#mutex.runExclusive(async () => {\n this.update((state) => {\n for (const [entropySourceId, groupedAddresses] of Object.entries(\n state.syncQueue,\n )) {\n const index = groupedAddresses.findIndex(\n ({ address }) => address === account,\n );\n if (index === -1) {\n continue;\n }\n groupedAddresses.splice(index, 1);\n if (groupedAddresses.length === 0) {\n delete state.syncQueue[entropySourceId];\n }\n break;\n }\n });\n });\n }\n}\n\n/**\n * Retrieves the entropy source ID from the given account, if it exists.\n *\n * @param account - The account from which to retrieve the entropy source ID.\n * @returns The entropy source ID, or null if it does not exist.\n */\nfunction getAccountEntropySourceId(account: InternalAccount): string | null {\n if (account.options.entropy?.type === 'mnemonic') {\n return account.options.entropy.id;\n }\n return null;\n}\n\n/**\n * Groups accounts by their entropy source ID.\n *\n * @param accounts - The accounts to group.\n * @returns An object where each key is an entropy source ID and each value is\n * an array of account addresses associated with that entropy source ID.\n */\nfunction groupAccountsByEntropySourceId(\n accounts: InternalAccount[],\n): Record<string, AccountWithScopes[]> {\n return accounts.reduce(\n (result: Record<string, AccountWithScopes[]>, account) => {\n const entropySourceId = getAccountEntropySourceId(account);\n const key = entropySourceId ?? 'null';\n if (!result[key]) {\n result[key] = [];\n }\n result[key].push({ address: account.address, scopes: account.scopes });\n return result;\n },\n {},\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ProfileMetricsController.cjs","sourceRoot":"","sources":["../src/ProfileMetricsController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AccountsControllerAccountAddedEvent,\n AccountsControllerAccountRemovedEvent,\n AccountsControllerGetStateAction,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport type {\n KeyringControllerLockEvent,\n KeyringControllerUnlockEvent,\n} from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { Messenger } from '@metamask/messenger';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport { TransactionControllerTransactionSubmittedEvent } from '@metamask/transaction-controller';\nimport { Duration, inMilliseconds, parseCaipChainId } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\n\nimport type { ProfileMetricsControllerMethodActions } from './ProfileMetricsController-method-action-types';\nimport type {\n AccountOwnershipProof,\n AccountWithScopes,\n} from './ProfileMetricsService';\nimport type { ProfileMetricsServiceMethodActions } from './ProfileMetricsService-method-action-types';\nimport type { ProofOfOwnershipServiceMethodActions } from './ProofOfOwnershipService-method-action-types';\nimport {\n canonicalizeAddress,\n ProofUnsupportedNamespaceError,\n} from './utils/canonicalize';\n\n/**\n * The name of the {@link ProfileMetricsController}, used to namespace the\n * controller's actions and events and to namespace the controller's state data\n * when composed with other controllers.\n */\nexport const controllerName = 'ProfileMetricsController';\n\n/**\n * The default delay duration before data is sent for the first time.\n */\nexport const DEFAULT_INITIAL_DELAY_DURATION = inMilliseconds(\n 1,\n Duration.Minute,\n);\n\n/**\n * Describes the shape of the state object for {@link ProfileMetricsController}.\n */\nexport type ProfileMetricsControllerState = {\n /**\n * Whether existing accounts have been added\n * to the queue.\n */\n initialEnqueueCompleted: boolean;\n /**\n * The queue of accounts to be synced.\n * Each key is an entropy source ID, and each value is an array of account\n * addresses associated with that entropy source. Accounts with no entropy\n * source ID are grouped under the key \"null\".\n */\n syncQueue: Record<string, AccountWithScopes[]>;\n /**\n * The timestamp when the first data sending can be attempted.\n */\n initialDelayEndTimestamp?: number;\n /**\n * Whether previously-synced accounts have been re-enqueued so their\n * proofs of ownership are submitted alongside everything else. Set on\n * the first unlock after upgrading to a version that signs proofs of\n * ownership; fresh installs flip this on their initial sync since the\n * first poll already attaches proofs.\n */\n proofBackfillEnqueued: boolean;\n};\n\n/**\n * The metadata for each property in {@link ProfileMetricsControllerState}.\n */\nconst profileMetricsControllerMetadata = {\n initialEnqueueCompleted: {\n persist: true,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: false,\n },\n syncQueue: {\n persist: true,\n includeInDebugSnapshot: false,\n includeInStateLogs: true,\n usedInUi: false,\n },\n initialDelayEndTimestamp: {\n persist: true,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: false,\n },\n proofBackfillEnqueued: {\n persist: true,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: false,\n },\n} satisfies StateMetadata<ProfileMetricsControllerState>;\n\n/**\n * Constructs the default {@link ProfileMetricsController} state. This allows\n * consumers to provide a partial state object when initializing the controller\n * and also helps in constructing complete state objects for this controller in\n * tests.\n *\n * @returns The default {@link ProfileMetricsController} state.\n */\nexport function getDefaultProfileMetricsControllerState(): ProfileMetricsControllerState {\n return {\n initialEnqueueCompleted: false,\n syncQueue: {},\n proofBackfillEnqueued: false,\n };\n}\n\nconst MESSENGER_EXPOSED_METHODS = ['skipInitialDelay'] as const;\n\n/**\n * Retrieves the state of the {@link ProfileMetricsController}.\n */\nexport type ProfileMetricsControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n ProfileMetricsControllerState\n>;\n\n/**\n * Actions that {@link ProfileMetricsControllerMessenger} exposes to other consumers.\n */\nexport type ProfileMetricsControllerActions =\n | ProfileMetricsControllerGetStateAction\n | ProfileMetricsControllerMethodActions;\n\n/**\n * Actions from other messengers that {@link ProfileMetricsControllerMessenger} calls.\n */\ntype AllowedActions =\n | ProfileMetricsServiceMethodActions\n | ProofOfOwnershipServiceMethodActions\n | AccountsControllerGetStateAction;\n\n/**\n * Published when the state of {@link ProfileMetricsController} changes.\n */\nexport type ProfileMetricsControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n ProfileMetricsControllerState\n >;\n\n/**\n * Events that {@link ProfileMetricsControllerMessenger} exposes to other consumers.\n */\nexport type ProfileMetricsControllerEvents =\n ProfileMetricsControllerStateChangeEvent;\n\n/**\n * Events from other messengers that {@link ProfileMetricsControllerMessenger} subscribes\n * to.\n */\ntype AllowedEvents =\n | KeyringControllerUnlockEvent\n | KeyringControllerLockEvent\n | AccountsControllerAccountAddedEvent\n | AccountsControllerAccountRemovedEvent\n | TransactionControllerTransactionSubmittedEvent;\n\n/**\n * The messenger restricted to actions and events accessed by\n * {@link ProfileMetricsController}.\n */\nexport type ProfileMetricsControllerMessenger = Messenger<\n typeof controllerName,\n ProfileMetricsControllerActions | AllowedActions,\n ProfileMetricsControllerEvents | AllowedEvents\n>;\n\n/**\n * Manages user profile metrics.\n *\n * For users who opt-in to metrics, this controller ensures we have metrics about their user\n * profile (metrics ID and accounts).\n */\nexport class ProfileMetricsController extends StaticIntervalPollingController()<\n typeof controllerName,\n ProfileMetricsControllerState,\n ProfileMetricsControllerMessenger\n> {\n readonly #mutex = new Mutex();\n\n readonly #assertUserOptedIn: () => boolean;\n\n readonly #getMetaMetricsId: () => string;\n\n readonly #initialDelayDuration: number;\n\n /**\n * Constructs a new {@link ProfileMetricsController}.\n *\n * @param args - The constructor arguments.\n * @param args.messenger - The messenger suited for this controller.\n * @param args.state - The desired state with which to initialize this\n * controller. Missing properties will be filled in with defaults.\n * @param args.assertUserOptedIn - A function that asserts whether the user has\n * opted in to user profile features. If the user has not opted in, sync\n * operations will be no-ops.\n * @param args.getMetaMetricsId - A function that returns the MetaMetrics ID\n * of the user.\n * @param args.interval - The interval, in milliseconds, at which the controller will\n * attempt to send user profile data. Defaults to 10 seconds.\n * @param args.initialDelayDuration - The delay duration before data is sent\n * for the first time, in milliseconds. Defaults to 10 minutes.\n */\n constructor({\n messenger,\n state,\n assertUserOptedIn,\n getMetaMetricsId,\n interval = 10 * 1000,\n initialDelayDuration = DEFAULT_INITIAL_DELAY_DURATION,\n }: {\n messenger: ProfileMetricsControllerMessenger;\n state?: Partial<ProfileMetricsControllerState>;\n interval?: number;\n assertUserOptedIn: () => boolean;\n getMetaMetricsId: () => string;\n initialDelayDuration?: number;\n }) {\n super({\n messenger,\n metadata: profileMetricsControllerMetadata,\n name: controllerName,\n state: {\n ...getDefaultProfileMetricsControllerState(),\n ...state,\n },\n });\n\n this.#assertUserOptedIn = assertUserOptedIn;\n this.#getMetaMetricsId = getMetaMetricsId;\n this.#initialDelayDuration = initialDelayDuration;\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n\n this.messenger.subscribe('KeyringController:unlock', () => {\n if (this.#assertUserOptedIn()) {\n // If the user has already opted in at the start of the session,\n // it must have opted in during onboarding, or during a previous session.\n this.skipInitialDelay();\n }\n this.#enqueueAccountsIfNeeded().catch(\n this.messenger.captureException ?? console.error,\n );\n this.startPolling(null);\n });\n\n this.messenger.subscribe('KeyringController:lock', () =>\n this.stopAllPolling(),\n );\n\n this.messenger.subscribe('TransactionController:transactionSubmitted', () =>\n this.skipInitialDelay(),\n );\n\n this.messenger.subscribe('AccountsController:accountAdded', (account) => {\n this.#addAccountToQueue(account).catch(console.error);\n });\n\n this.messenger.subscribe('AccountsController:accountRemoved', (account) => {\n this.#removeAccountFromQueue(account).catch(console.error);\n });\n\n this.setIntervalLength(interval);\n }\n\n /**\n * Skip the initial delay period by setting the end timestamp to the current time.\n * Metrics will be sent on the next poll.\n */\n skipInitialDelay(): void {\n this.update((state) => {\n state.initialDelayEndTimestamp = Date.now();\n });\n }\n\n /**\n * Execute a single poll to sync user profile data.\n *\n * The queued accounts are sent to the ProfileMetricsService, each with\n * a proof of ownership when one can be produced (see {@link #attachProofs}),\n * and the sync queue is cleared. This operation is mutexed to prevent\n * concurrent executions.\n *\n * @returns A promise that resolves when the poll is complete.\n */\n async _executePoll(): Promise<void> {\n await this.#mutex.runExclusive(async () => {\n if (!this.#assertUserOptedIn()) {\n return;\n }\n this.#setInitialDelayEndTimestampIfNull();\n if (!this.#isInitialDelayComplete()) {\n return;\n }\n const fullAccountsByAddress = this.#getFullAccountsByAddress();\n for (const [entropySourceId, accounts] of Object.entries(\n this.state.syncQueue,\n )) {\n const normalizedEntropySourceId =\n entropySourceId === 'null' ? null : entropySourceId;\n // Skip proof-of-ownership for accounts without an entropy source\n const accountsWithProofs =\n normalizedEntropySourceId === null\n ? accounts\n : await this.#attachProofs(\n accounts,\n fullAccountsByAddress,\n normalizedEntropySourceId,\n );\n try {\n await this.messenger.call('ProfileMetricsService:submitMetrics', {\n metametricsId: this.#getMetaMetricsId(),\n entropySourceId: normalizedEntropySourceId,\n accounts: accountsWithProofs,\n });\n this.update((state) => {\n delete state.syncQueue[entropySourceId];\n });\n } catch (error) {\n // We want to log the error but continue processing other batches.\n console.error(\n `Failed to submit profile metrics for entropy source ID ${entropySourceId}:`,\n error,\n );\n }\n }\n });\n }\n\n /**\n * Attach a proof of ownership to each account in a single entropy-source\n * batch when possible, canonicalizing the address along the way.\n *\n * Per-account failures (unknown namespace, snap missing the\n * `signProofOfOwnership` method, snap rejection) and whole-batch nonce\n * failures are caught and downgraded to \"submit without a proof\" so the\n * batch still goes through and the proof is retried on the next poll.\n *\n * @param accounts - The queued accounts for a single batch.\n * @param fullAccountsByAddress - Live `InternalAccount` lookup keyed by address.\n * @param entropySourceId - The entropy source ID for this batch. Callers\n * are expected to short-circuit before invoking this method when the\n * batch has no entropy source; see `_executePoll` for why.\n * @returns The accounts with `proof` populated where signing succeeded.\n */\n async #attachProofs(\n accounts: AccountWithScopes[],\n fullAccountsByAddress: Map<string, InternalAccount>,\n entropySourceId: string,\n ): Promise<AccountWithScopes[]> {\n const candidates = new Map<\n string,\n { account: InternalAccount; canonicalAddress: string }\n >();\n const identifiers = new Set<string>();\n for (const queued of accounts) {\n const fullAccount = fullAccountsByAddress.get(queued.address);\n if (!fullAccount) {\n continue;\n }\n try {\n const [scope] = fullAccount.scopes;\n if (!scope) {\n throw new Error(`Scope not found for account ${fullAccount.id}`);\n }\n const { namespace } = parseCaipChainId(scope);\n const canonicalAddress = canonicalizeAddress(\n fullAccount.address,\n namespace,\n );\n candidates.set(queued.address, {\n account: fullAccount,\n canonicalAddress,\n });\n identifiers.add(canonicalAddress);\n } catch (error) {\n // Unsupported namespaces are an expected pass-through; anything\n // else is logged so a new namespace doesn't go unnoticed.\n if (!(error instanceof ProofUnsupportedNamespaceError)) {\n console.error(`Skipping proof for account ${fullAccount.id}:`, error);\n }\n }\n }\n\n if (candidates.size === 0) {\n return accounts;\n }\n\n let nonces: Record<string, string> = {};\n try {\n nonces = await this.messenger.call('ProfileMetricsService:fetchNonces', {\n identifiers: [...identifiers],\n entropySourceId,\n });\n } catch (error) {\n console.error(\n `Failed to fetch proof-of-ownership nonces for entropy source ID ${entropySourceId}:`,\n error,\n );\n }\n\n return await Promise.all(\n accounts.map(async (queued): Promise<AccountWithScopes> => {\n const candidate = candidates.get(queued.address);\n if (!candidate) {\n return queued;\n }\n const nonce = nonces[candidate.canonicalAddress];\n if (!nonce) {\n return { ...queued, address: candidate.canonicalAddress };\n }\n let proof: AccountOwnershipProof;\n try {\n proof = await this.messenger.call('ProofOfOwnershipService:sign', {\n account: candidate.account,\n nonce,\n });\n } catch (error) {\n console.error(\n `Failed to sign proof of ownership for account ${candidate.account.id}:`,\n error,\n );\n return { ...queued, address: candidate.canonicalAddress };\n }\n return {\n address: candidate.canonicalAddress,\n scopes: queued.scopes,\n proof,\n };\n }),\n );\n }\n\n /**\n * Snapshot the live `InternalAccount` map keyed by address for the\n * current poll.\n *\n * @returns A map of address → `InternalAccount`.\n */\n #getFullAccountsByAddress(): Map<string, InternalAccount> {\n const byAddress = new Map<string, InternalAccount>();\n const accountsState = this.messenger.call('AccountsController:getState');\n for (const account of Object.values(\n accountsState.internalAccounts.accounts,\n )) {\n byAddress.set(account.address, account);\n }\n return byAddress;\n }\n\n /**\n * Enqueue all currently-known accounts onto the sync queue if needed.\n * Single entry point covering both the fresh-install first sync and\n * the one-time proof-of-ownership backfill for users upgrading.\n *\n * Bails for opted-out users (the poll wouldn't drain the queue\n * anyway), and bails once both bootstrap steps have already run.\n * Otherwise enqueues all known accounts and flips both flags so this\n * becomes a permanent no-op for the lifetime of the install.\n */\n async #enqueueAccountsIfNeeded(): Promise<void> {\n await this.#mutex.runExclusive(async () => {\n if (!this.#assertUserOptedIn()) {\n return;\n }\n if (\n this.state.initialEnqueueCompleted &&\n this.state.proofBackfillEnqueued\n ) {\n return;\n }\n const groupedAccounts = groupAccountsByEntropySourceId(\n Object.values(\n this.messenger.call('AccountsController:getState').internalAccounts\n .accounts,\n ),\n );\n this.update((state) => {\n // Replace the queue rather than append. `AccountsController` is\n // the source of truth and the queue is otherwise kept in sync\n // with it via the `accountAdded` / `accountRemoved` subscriptions,\n // so assigning here avoids duplicating entries that survived from\n // a prior session or were pushed earlier in this same unlock\n // cycle. Duplicates would matter because nonces are single-use:\n // letting one through causes `#attachProofs` to sign and submit\n // twice with the same nonce.\n state.syncQueue = groupedAccounts;\n state.initialEnqueueCompleted = true;\n state.proofBackfillEnqueued = true;\n });\n });\n }\n\n /**\n * Set the initial delay end timestamp if it is not already set.\n */\n #setInitialDelayEndTimestampIfNull(): void {\n this.update((state) => {\n state.initialDelayEndTimestamp ??=\n Date.now() + this.#initialDelayDuration;\n });\n }\n\n /**\n * Check if the initial delay end timestamp is in the past.\n *\n * @returns True if the initial delay period has completed, false otherwise.\n */\n #isInitialDelayComplete(): boolean {\n return (\n this.state.initialDelayEndTimestamp !== undefined &&\n Date.now() >= this.state.initialDelayEndTimestamp\n );\n }\n\n /**\n * Queue the given account to be synced at the next poll.\n *\n * @param account - The account to sync.\n */\n async #addAccountToQueue(account: InternalAccount): Promise<void> {\n await this.#mutex.runExclusive(async () => {\n this.update((state) => {\n const entropySourceId = getAccountEntropySourceId(account) ?? 'null';\n if (!state.syncQueue[entropySourceId]) {\n state.syncQueue[entropySourceId] = [];\n }\n state.syncQueue[entropySourceId].push({\n address: account.address,\n scopes: account.scopes,\n });\n });\n });\n }\n\n /**\n * Remove the given account from the sync queue.\n *\n * @param account - The account address to remove.\n */\n async #removeAccountFromQueue(account: string): Promise<void> {\n await this.#mutex.runExclusive(async () => {\n this.update((state) => {\n for (const [entropySourceId, groupedAddresses] of Object.entries(\n state.syncQueue,\n )) {\n const index = groupedAddresses.findIndex(\n ({ address }) => address === account,\n );\n if (index === -1) {\n continue;\n }\n groupedAddresses.splice(index, 1);\n if (groupedAddresses.length === 0) {\n delete state.syncQueue[entropySourceId];\n }\n break;\n }\n });\n });\n }\n}\n\n/**\n * Retrieves the entropy source ID from the given account, if it exists.\n *\n * @param account - The account from which to retrieve the entropy source ID.\n * @returns The entropy source ID, or null if it does not exist.\n */\nfunction getAccountEntropySourceId(account: InternalAccount): string | null {\n if (account.options.entropy?.type === 'mnemonic') {\n return account.options.entropy.id;\n }\n return null;\n}\n\n/**\n * Groups accounts by their entropy source ID.\n *\n * @param accounts - The accounts to group.\n * @returns An object where each key is an entropy source ID and each value is\n * an array of account addresses associated with that entropy source ID.\n */\nfunction groupAccountsByEntropySourceId(\n accounts: InternalAccount[],\n): Record<string, AccountWithScopes[]> {\n return accounts.reduce(\n (result: Record<string, AccountWithScopes[]>, account) => {\n const entropySourceId = getAccountEntropySourceId(account);\n const key = entropySourceId ?? 'null';\n if (!result[key]) {\n result[key] = [];\n }\n result[key].push({ address: account.address, scopes: account.scopes });\n return result;\n },\n {},\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProfileMetricsController.d.cts","sourceRoot":"","sources":["../src/ProfileMetricsController.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EACV,mCAAmC,EACnC,qCAAqC,EACrC,gCAAgC,EACjC,sCAAsC;AACvC,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAE3B,kCAAkC;AACnC,OAAO,KAAK,EACV,0BAA0B,EAC1B,4BAA4B,EAC7B,qCAAqC;AAEtC,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AAErD,OAAO,EAAE,8CAA8C,EAAE,yCAAyC;AAIlG,OAAO,KAAK,EAAE,qCAAqC,EAAE,2DAAuD;AAC5G,OAAO,KAAK,EAEV,iBAAiB,EAClB,oCAAgC;AACjC,OAAO,KAAK,EAAE,kCAAkC,EAAE,wDAAoD;AACtG,OAAO,KAAK,EAAE,oCAAoC,EAAE,0DAAsD;AAM1G;;;;GAIG;AACH,eAAO,MAAM,cAAc,6BAA6B,CAAC;AAEzD;;GAEG;AACH,eAAO,MAAM,8BAA8B,QAG1C,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,6BAA6B,GAAG;IAC1C;;;OAGG;IACH,uBAAuB,EAAE,OAAO,CAAC;IACjC;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC,CAAC;IAC/C;;OAEG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC;;;;;;OAMG;IACH,qBAAqB,EAAE,OAAO,CAAC;CAChC,CAAC;AAgCF;;;;;;;GAOG;AACH,wBAAgB,uCAAuC,IAAI,6BAA6B,CAMvF;AAID;;GAEG;AACH,MAAM,MAAM,sCAAsC,GAAG,wBAAwB,CAC3E,OAAO,cAAc,EACrB,6BAA6B,CAC9B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,+BAA+B,GACvC,sCAAsC,GACtC,qCAAqC,CAAC;AAE1C;;GAEG;AACH,KAAK,cAAc,GACf,kCAAkC,GAClC,oCAAoC,GACpC,gCAAgC,CAAC;AAErC;;GAEG;AACH,MAAM,MAAM,wCAAwC,GAClD,0BAA0B,CACxB,OAAO,cAAc,EACrB,6BAA6B,CAC9B,CAAC;AAEJ;;GAEG;AACH,MAAM,MAAM,8BAA8B,GACxC,wCAAwC,CAAC;AAE3C;;;GAGG;AACH,KAAK,aAAa,GACd,4BAA4B,GAC5B,0BAA0B,GAC1B,mCAAmC,GACnC,qCAAqC,GACrC,8CAA8C,CAAC;AAEnD;;;GAGG;AACH,MAAM,MAAM,iCAAiC,GAAG,SAAS,CACvD,OAAO,cAAc,EACrB,+BAA+B,GAAG,cAAc,EAChD,8BAA8B,GAAG,aAAa,CAC/C,CAAC;;;iDAtJF;;;;OAIG;;;;;;;;;;;;;AAoJH;;;;;GAKG;AACH,qBAAa,wBAAyB,SAAQ,8BAC5C,OAAO,cAAc,EACrB,6BAA6B,EAC7B,iCAAiC,CAClC;;IASC;;;;;;;;;;;;;;;;OAgBG;gBACS,EACV,SAAS,EACT,KAAK,EACL,iBAAiB,EACjB,gBAAgB,EAChB,QAAoB,EACpB,oBAAqD,GACtD,EAAE;QACD,SAAS,EAAE,iCAAiC,CAAC;QAC7C,KAAK,CAAC,EAAE,OAAO,CAAC,6BAA6B,CAAC,CAAC;QAC/C,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,iBAAiB,EAAE,MAAM,OAAO,CAAC;QACjC,gBAAgB,EAAE,MAAM,MAAM,CAAC;QAC/B,oBAAoB,CAAC,EAAE,MAAM,CAAC;KAC/B;IAmDD;;;OAGG;IACH,gBAAgB,IAAI,IAAI;IAMxB;;;;;;;;;OASG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"ProfileMetricsController.d.cts","sourceRoot":"","sources":["../src/ProfileMetricsController.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EACV,mCAAmC,EACnC,qCAAqC,EACrC,gCAAgC,EACjC,sCAAsC;AACvC,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAE3B,kCAAkC;AACnC,OAAO,KAAK,EACV,0BAA0B,EAC1B,4BAA4B,EAC7B,qCAAqC;AAEtC,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AAErD,OAAO,EAAE,8CAA8C,EAAE,yCAAyC;AAIlG,OAAO,KAAK,EAAE,qCAAqC,EAAE,2DAAuD;AAC5G,OAAO,KAAK,EAEV,iBAAiB,EAClB,oCAAgC;AACjC,OAAO,KAAK,EAAE,kCAAkC,EAAE,wDAAoD;AACtG,OAAO,KAAK,EAAE,oCAAoC,EAAE,0DAAsD;AAM1G;;;;GAIG;AACH,eAAO,MAAM,cAAc,6BAA6B,CAAC;AAEzD;;GAEG;AACH,eAAO,MAAM,8BAA8B,QAG1C,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,6BAA6B,GAAG;IAC1C;;;OAGG;IACH,uBAAuB,EAAE,OAAO,CAAC;IACjC;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC,CAAC;IAC/C;;OAEG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC;;;;;;OAMG;IACH,qBAAqB,EAAE,OAAO,CAAC;CAChC,CAAC;AAgCF;;;;;;;GAOG;AACH,wBAAgB,uCAAuC,IAAI,6BAA6B,CAMvF;AAID;;GAEG;AACH,MAAM,MAAM,sCAAsC,GAAG,wBAAwB,CAC3E,OAAO,cAAc,EACrB,6BAA6B,CAC9B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,+BAA+B,GACvC,sCAAsC,GACtC,qCAAqC,CAAC;AAE1C;;GAEG;AACH,KAAK,cAAc,GACf,kCAAkC,GAClC,oCAAoC,GACpC,gCAAgC,CAAC;AAErC;;GAEG;AACH,MAAM,MAAM,wCAAwC,GAClD,0BAA0B,CACxB,OAAO,cAAc,EACrB,6BAA6B,CAC9B,CAAC;AAEJ;;GAEG;AACH,MAAM,MAAM,8BAA8B,GACxC,wCAAwC,CAAC;AAE3C;;;GAGG;AACH,KAAK,aAAa,GACd,4BAA4B,GAC5B,0BAA0B,GAC1B,mCAAmC,GACnC,qCAAqC,GACrC,8CAA8C,CAAC;AAEnD;;;GAGG;AACH,MAAM,MAAM,iCAAiC,GAAG,SAAS,CACvD,OAAO,cAAc,EACrB,+BAA+B,GAAG,cAAc,EAChD,8BAA8B,GAAG,aAAa,CAC/C,CAAC;;;iDAtJF;;;;OAIG;;;;;;;;;;;;;AAoJH;;;;;GAKG;AACH,qBAAa,wBAAyB,SAAQ,8BAC5C,OAAO,cAAc,EACrB,6BAA6B,EAC7B,iCAAiC,CAClC;;IASC;;;;;;;;;;;;;;;;OAgBG;gBACS,EACV,SAAS,EACT,KAAK,EACL,iBAAiB,EACjB,gBAAgB,EAChB,QAAoB,EACpB,oBAAqD,GACtD,EAAE;QACD,SAAS,EAAE,iCAAiC,CAAC;QAC7C,KAAK,CAAC,EAAE,OAAO,CAAC,6BAA6B,CAAC,CAAC;QAC/C,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,iBAAiB,EAAE,MAAM,OAAO,CAAC;QACjC,gBAAgB,EAAE,MAAM,MAAM,CAAC;QAC/B,oBAAoB,CAAC,EAAE,MAAM,CAAC;KAC/B;IAmDD;;;OAGG;IACH,gBAAgB,IAAI,IAAI;IAMxB;;;;;;;;;OASG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;CAoRpC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProfileMetricsController.d.mts","sourceRoot":"","sources":["../src/ProfileMetricsController.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EACV,mCAAmC,EACnC,qCAAqC,EACrC,gCAAgC,EACjC,sCAAsC;AACvC,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAE3B,kCAAkC;AACnC,OAAO,KAAK,EACV,0BAA0B,EAC1B,4BAA4B,EAC7B,qCAAqC;AAEtC,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AAErD,OAAO,EAAE,8CAA8C,EAAE,yCAAyC;AAIlG,OAAO,KAAK,EAAE,qCAAqC,EAAE,2DAAuD;AAC5G,OAAO,KAAK,EAEV,iBAAiB,EAClB,oCAAgC;AACjC,OAAO,KAAK,EAAE,kCAAkC,EAAE,wDAAoD;AACtG,OAAO,KAAK,EAAE,oCAAoC,EAAE,0DAAsD;AAM1G;;;;GAIG;AACH,eAAO,MAAM,cAAc,6BAA6B,CAAC;AAEzD;;GAEG;AACH,eAAO,MAAM,8BAA8B,QAG1C,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,6BAA6B,GAAG;IAC1C;;;OAGG;IACH,uBAAuB,EAAE,OAAO,CAAC;IACjC;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC,CAAC;IAC/C;;OAEG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC;;;;;;OAMG;IACH,qBAAqB,EAAE,OAAO,CAAC;CAChC,CAAC;AAgCF;;;;;;;GAOG;AACH,wBAAgB,uCAAuC,IAAI,6BAA6B,CAMvF;AAID;;GAEG;AACH,MAAM,MAAM,sCAAsC,GAAG,wBAAwB,CAC3E,OAAO,cAAc,EACrB,6BAA6B,CAC9B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,+BAA+B,GACvC,sCAAsC,GACtC,qCAAqC,CAAC;AAE1C;;GAEG;AACH,KAAK,cAAc,GACf,kCAAkC,GAClC,oCAAoC,GACpC,gCAAgC,CAAC;AAErC;;GAEG;AACH,MAAM,MAAM,wCAAwC,GAClD,0BAA0B,CACxB,OAAO,cAAc,EACrB,6BAA6B,CAC9B,CAAC;AAEJ;;GAEG;AACH,MAAM,MAAM,8BAA8B,GACxC,wCAAwC,CAAC;AAE3C;;;GAGG;AACH,KAAK,aAAa,GACd,4BAA4B,GAC5B,0BAA0B,GAC1B,mCAAmC,GACnC,qCAAqC,GACrC,8CAA8C,CAAC;AAEnD;;;GAGG;AACH,MAAM,MAAM,iCAAiC,GAAG,SAAS,CACvD,OAAO,cAAc,EACrB,+BAA+B,GAAG,cAAc,EAChD,8BAA8B,GAAG,aAAa,CAC/C,CAAC;;;iDAtJF;;;;OAIG;;;;;;;;;;;;;AAoJH;;;;;GAKG;AACH,qBAAa,wBAAyB,SAAQ,8BAC5C,OAAO,cAAc,EACrB,6BAA6B,EAC7B,iCAAiC,CAClC;;IASC;;;;;;;;;;;;;;;;OAgBG;gBACS,EACV,SAAS,EACT,KAAK,EACL,iBAAiB,EACjB,gBAAgB,EAChB,QAAoB,EACpB,oBAAqD,GACtD,EAAE;QACD,SAAS,EAAE,iCAAiC,CAAC;QAC7C,KAAK,CAAC,EAAE,OAAO,CAAC,6BAA6B,CAAC,CAAC;QAC/C,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,iBAAiB,EAAE,MAAM,OAAO,CAAC;QACjC,gBAAgB,EAAE,MAAM,MAAM,CAAC;QAC/B,oBAAoB,CAAC,EAAE,MAAM,CAAC;KAC/B;IAmDD;;;OAGG;IACH,gBAAgB,IAAI,IAAI;IAMxB;;;;;;;;;OASG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"ProfileMetricsController.d.mts","sourceRoot":"","sources":["../src/ProfileMetricsController.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EACV,mCAAmC,EACnC,qCAAqC,EACrC,gCAAgC,EACjC,sCAAsC;AACvC,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAE3B,kCAAkC;AACnC,OAAO,KAAK,EACV,0BAA0B,EAC1B,4BAA4B,EAC7B,qCAAqC;AAEtC,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AAErD,OAAO,EAAE,8CAA8C,EAAE,yCAAyC;AAIlG,OAAO,KAAK,EAAE,qCAAqC,EAAE,2DAAuD;AAC5G,OAAO,KAAK,EAEV,iBAAiB,EAClB,oCAAgC;AACjC,OAAO,KAAK,EAAE,kCAAkC,EAAE,wDAAoD;AACtG,OAAO,KAAK,EAAE,oCAAoC,EAAE,0DAAsD;AAM1G;;;;GAIG;AACH,eAAO,MAAM,cAAc,6BAA6B,CAAC;AAEzD;;GAEG;AACH,eAAO,MAAM,8BAA8B,QAG1C,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,6BAA6B,GAAG;IAC1C;;;OAGG;IACH,uBAAuB,EAAE,OAAO,CAAC;IACjC;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC,CAAC;IAC/C;;OAEG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC;;;;;;OAMG;IACH,qBAAqB,EAAE,OAAO,CAAC;CAChC,CAAC;AAgCF;;;;;;;GAOG;AACH,wBAAgB,uCAAuC,IAAI,6BAA6B,CAMvF;AAID;;GAEG;AACH,MAAM,MAAM,sCAAsC,GAAG,wBAAwB,CAC3E,OAAO,cAAc,EACrB,6BAA6B,CAC9B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,+BAA+B,GACvC,sCAAsC,GACtC,qCAAqC,CAAC;AAE1C;;GAEG;AACH,KAAK,cAAc,GACf,kCAAkC,GAClC,oCAAoC,GACpC,gCAAgC,CAAC;AAErC;;GAEG;AACH,MAAM,MAAM,wCAAwC,GAClD,0BAA0B,CACxB,OAAO,cAAc,EACrB,6BAA6B,CAC9B,CAAC;AAEJ;;GAEG;AACH,MAAM,MAAM,8BAA8B,GACxC,wCAAwC,CAAC;AAE3C;;;GAGG;AACH,KAAK,aAAa,GACd,4BAA4B,GAC5B,0BAA0B,GAC1B,mCAAmC,GACnC,qCAAqC,GACrC,8CAA8C,CAAC;AAEnD;;;GAGG;AACH,MAAM,MAAM,iCAAiC,GAAG,SAAS,CACvD,OAAO,cAAc,EACrB,+BAA+B,GAAG,cAAc,EAChD,8BAA8B,GAAG,aAAa,CAC/C,CAAC;;;iDAtJF;;;;OAIG;;;;;;;;;;;;;AAoJH;;;;;GAKG;AACH,qBAAa,wBAAyB,SAAQ,8BAC5C,OAAO,cAAc,EACrB,6BAA6B,EAC7B,iCAAiC,CAClC;;IASC;;;;;;;;;;;;;;;;OAgBG;gBACS,EACV,SAAS,EACT,KAAK,EACL,iBAAiB,EACjB,gBAAgB,EAChB,QAAoB,EACpB,oBAAqD,GACtD,EAAE;QACD,SAAS,EAAE,iCAAiC,CAAC;QAC7C,KAAK,CAAC,EAAE,OAAO,CAAC,6BAA6B,CAAC,CAAC;QAC/C,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,iBAAiB,EAAE,MAAM,OAAO,CAAC;QACjC,gBAAgB,EAAE,MAAM,MAAM,CAAC;QAC/B,oBAAoB,CAAC,EAAE,MAAM,CAAC;KAC/B;IAmDD;;;OAGG;IACH,gBAAgB,IAAI,IAAI;IAMxB;;;;;;;;;OASG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;CAoRpC"}
|
|
@@ -162,7 +162,10 @@ export class ProfileMetricsController extends StaticIntervalPollingController()
|
|
|
162
162
|
const fullAccountsByAddress = __classPrivateFieldGet(this, _ProfileMetricsController_instances, "m", _ProfileMetricsController_getFullAccountsByAddress).call(this);
|
|
163
163
|
for (const [entropySourceId, accounts] of Object.entries(this.state.syncQueue)) {
|
|
164
164
|
const normalizedEntropySourceId = entropySourceId === 'null' ? null : entropySourceId;
|
|
165
|
-
|
|
165
|
+
// Skip proof-of-ownership for accounts without an entropy source
|
|
166
|
+
const accountsWithProofs = normalizedEntropySourceId === null
|
|
167
|
+
? accounts
|
|
168
|
+
: await __classPrivateFieldGet(this, _ProfileMetricsController_instances, "m", _ProfileMetricsController_attachProofs).call(this, accounts, fullAccountsByAddress, normalizedEntropySourceId);
|
|
166
169
|
try {
|
|
167
170
|
await this.messenger.call('ProfileMetricsService:submitMetrics', {
|
|
168
171
|
metametricsId: __classPrivateFieldGet(this, _ProfileMetricsController_getMetaMetricsId, "f").call(this),
|
|
@@ -193,7 +196,9 @@ _ProfileMetricsController_mutex = new WeakMap(), _ProfileMetricsController_asser
|
|
|
193
196
|
*
|
|
194
197
|
* @param accounts - The queued accounts for a single batch.
|
|
195
198
|
* @param fullAccountsByAddress - Live `InternalAccount` lookup keyed by address.
|
|
196
|
-
* @param entropySourceId - The entropy source ID for this batch.
|
|
199
|
+
* @param entropySourceId - The entropy source ID for this batch. Callers
|
|
200
|
+
* are expected to short-circuit before invoking this method when the
|
|
201
|
+
* batch has no entropy source; see `_executePoll` for why.
|
|
197
202
|
* @returns The accounts with `proof` populated where signing succeeded.
|
|
198
203
|
*/
|
|
199
204
|
async function _ProfileMetricsController_attachProofs(accounts, fullAccountsByAddress, entropySourceId) {
|
|
@@ -236,7 +241,7 @@ async function _ProfileMetricsController_attachProofs(accounts, fullAccountsByAd
|
|
|
236
241
|
});
|
|
237
242
|
}
|
|
238
243
|
catch (error) {
|
|
239
|
-
console.error(`Failed to fetch proof-of-ownership nonces for entropy source ID ${entropySourceId
|
|
244
|
+
console.error(`Failed to fetch proof-of-ownership nonces for entropy source ID ${entropySourceId}:`, error);
|
|
240
245
|
}
|
|
241
246
|
return await Promise.all(accounts.map(async (queued) => {
|
|
242
247
|
const candidate = candidates.get(queued.address);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProfileMetricsController.mjs","sourceRoot":"","sources":["../src/ProfileMetricsController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AccountsControllerAccountAddedEvent,\n AccountsControllerAccountRemovedEvent,\n AccountsControllerGetStateAction,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport type {\n KeyringControllerLockEvent,\n KeyringControllerUnlockEvent,\n} from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { Messenger } from '@metamask/messenger';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport { TransactionControllerTransactionSubmittedEvent } from '@metamask/transaction-controller';\nimport { Duration, inMilliseconds, parseCaipChainId } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\n\nimport type { ProfileMetricsControllerMethodActions } from './ProfileMetricsController-method-action-types';\nimport type {\n AccountOwnershipProof,\n AccountWithScopes,\n} from './ProfileMetricsService';\nimport type { ProfileMetricsServiceMethodActions } from './ProfileMetricsService-method-action-types';\nimport type { ProofOfOwnershipServiceMethodActions } from './ProofOfOwnershipService-method-action-types';\nimport {\n canonicalizeAddress,\n ProofUnsupportedNamespaceError,\n} from './utils/canonicalize';\n\n/**\n * The name of the {@link ProfileMetricsController}, used to namespace the\n * controller's actions and events and to namespace the controller's state data\n * when composed with other controllers.\n */\nexport const controllerName = 'ProfileMetricsController';\n\n/**\n * The default delay duration before data is sent for the first time.\n */\nexport const DEFAULT_INITIAL_DELAY_DURATION = inMilliseconds(\n 1,\n Duration.Minute,\n);\n\n/**\n * Describes the shape of the state object for {@link ProfileMetricsController}.\n */\nexport type ProfileMetricsControllerState = {\n /**\n * Whether existing accounts have been added\n * to the queue.\n */\n initialEnqueueCompleted: boolean;\n /**\n * The queue of accounts to be synced.\n * Each key is an entropy source ID, and each value is an array of account\n * addresses associated with that entropy source. Accounts with no entropy\n * source ID are grouped under the key \"null\".\n */\n syncQueue: Record<string, AccountWithScopes[]>;\n /**\n * The timestamp when the first data sending can be attempted.\n */\n initialDelayEndTimestamp?: number;\n /**\n * Whether previously-synced accounts have been re-enqueued so their\n * proofs of ownership are submitted alongside everything else. Set on\n * the first unlock after upgrading to a version that signs proofs of\n * ownership; fresh installs flip this on their initial sync since the\n * first poll already attaches proofs.\n */\n proofBackfillEnqueued: boolean;\n};\n\n/**\n * The metadata for each property in {@link ProfileMetricsControllerState}.\n */\nconst profileMetricsControllerMetadata = {\n initialEnqueueCompleted: {\n persist: true,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: false,\n },\n syncQueue: {\n persist: true,\n includeInDebugSnapshot: false,\n includeInStateLogs: true,\n usedInUi: false,\n },\n initialDelayEndTimestamp: {\n persist: true,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: false,\n },\n proofBackfillEnqueued: {\n persist: true,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: false,\n },\n} satisfies StateMetadata<ProfileMetricsControllerState>;\n\n/**\n * Constructs the default {@link ProfileMetricsController} state. This allows\n * consumers to provide a partial state object when initializing the controller\n * and also helps in constructing complete state objects for this controller in\n * tests.\n *\n * @returns The default {@link ProfileMetricsController} state.\n */\nexport function getDefaultProfileMetricsControllerState(): ProfileMetricsControllerState {\n return {\n initialEnqueueCompleted: false,\n syncQueue: {},\n proofBackfillEnqueued: false,\n };\n}\n\nconst MESSENGER_EXPOSED_METHODS = ['skipInitialDelay'] as const;\n\n/**\n * Retrieves the state of the {@link ProfileMetricsController}.\n */\nexport type ProfileMetricsControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n ProfileMetricsControllerState\n>;\n\n/**\n * Actions that {@link ProfileMetricsControllerMessenger} exposes to other consumers.\n */\nexport type ProfileMetricsControllerActions =\n | ProfileMetricsControllerGetStateAction\n | ProfileMetricsControllerMethodActions;\n\n/**\n * Actions from other messengers that {@link ProfileMetricsControllerMessenger} calls.\n */\ntype AllowedActions =\n | ProfileMetricsServiceMethodActions\n | ProofOfOwnershipServiceMethodActions\n | AccountsControllerGetStateAction;\n\n/**\n * Published when the state of {@link ProfileMetricsController} changes.\n */\nexport type ProfileMetricsControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n ProfileMetricsControllerState\n >;\n\n/**\n * Events that {@link ProfileMetricsControllerMessenger} exposes to other consumers.\n */\nexport type ProfileMetricsControllerEvents =\n ProfileMetricsControllerStateChangeEvent;\n\n/**\n * Events from other messengers that {@link ProfileMetricsControllerMessenger} subscribes\n * to.\n */\ntype AllowedEvents =\n | KeyringControllerUnlockEvent\n | KeyringControllerLockEvent\n | AccountsControllerAccountAddedEvent\n | AccountsControllerAccountRemovedEvent\n | TransactionControllerTransactionSubmittedEvent;\n\n/**\n * The messenger restricted to actions and events accessed by\n * {@link ProfileMetricsController}.\n */\nexport type ProfileMetricsControllerMessenger = Messenger<\n typeof controllerName,\n ProfileMetricsControllerActions | AllowedActions,\n ProfileMetricsControllerEvents | AllowedEvents\n>;\n\n/**\n * Manages user profile metrics.\n *\n * For users who opt-in to metrics, this controller ensures we have metrics about their user\n * profile (metrics ID and accounts).\n */\nexport class ProfileMetricsController extends StaticIntervalPollingController()<\n typeof controllerName,\n ProfileMetricsControllerState,\n ProfileMetricsControllerMessenger\n> {\n readonly #mutex = new Mutex();\n\n readonly #assertUserOptedIn: () => boolean;\n\n readonly #getMetaMetricsId: () => string;\n\n readonly #initialDelayDuration: number;\n\n /**\n * Constructs a new {@link ProfileMetricsController}.\n *\n * @param args - The constructor arguments.\n * @param args.messenger - The messenger suited for this controller.\n * @param args.state - The desired state with which to initialize this\n * controller. Missing properties will be filled in with defaults.\n * @param args.assertUserOptedIn - A function that asserts whether the user has\n * opted in to user profile features. If the user has not opted in, sync\n * operations will be no-ops.\n * @param args.getMetaMetricsId - A function that returns the MetaMetrics ID\n * of the user.\n * @param args.interval - The interval, in milliseconds, at which the controller will\n * attempt to send user profile data. Defaults to 10 seconds.\n * @param args.initialDelayDuration - The delay duration before data is sent\n * for the first time, in milliseconds. Defaults to 10 minutes.\n */\n constructor({\n messenger,\n state,\n assertUserOptedIn,\n getMetaMetricsId,\n interval = 10 * 1000,\n initialDelayDuration = DEFAULT_INITIAL_DELAY_DURATION,\n }: {\n messenger: ProfileMetricsControllerMessenger;\n state?: Partial<ProfileMetricsControllerState>;\n interval?: number;\n assertUserOptedIn: () => boolean;\n getMetaMetricsId: () => string;\n initialDelayDuration?: number;\n }) {\n super({\n messenger,\n metadata: profileMetricsControllerMetadata,\n name: controllerName,\n state: {\n ...getDefaultProfileMetricsControllerState(),\n ...state,\n },\n });\n\n this.#assertUserOptedIn = assertUserOptedIn;\n this.#getMetaMetricsId = getMetaMetricsId;\n this.#initialDelayDuration = initialDelayDuration;\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n\n this.messenger.subscribe('KeyringController:unlock', () => {\n if (this.#assertUserOptedIn()) {\n // If the user has already opted in at the start of the session,\n // it must have opted in during onboarding, or during a previous session.\n this.skipInitialDelay();\n }\n this.#enqueueAccountsIfNeeded().catch(\n this.messenger.captureException ?? console.error,\n );\n this.startPolling(null);\n });\n\n this.messenger.subscribe('KeyringController:lock', () =>\n this.stopAllPolling(),\n );\n\n this.messenger.subscribe('TransactionController:transactionSubmitted', () =>\n this.skipInitialDelay(),\n );\n\n this.messenger.subscribe('AccountsController:accountAdded', (account) => {\n this.#addAccountToQueue(account).catch(console.error);\n });\n\n this.messenger.subscribe('AccountsController:accountRemoved', (account) => {\n this.#removeAccountFromQueue(account).catch(console.error);\n });\n\n this.setIntervalLength(interval);\n }\n\n /**\n * Skip the initial delay period by setting the end timestamp to the current time.\n * Metrics will be sent on the next poll.\n */\n skipInitialDelay(): void {\n this.update((state) => {\n state.initialDelayEndTimestamp = Date.now();\n });\n }\n\n /**\n * Execute a single poll to sync user profile data.\n *\n * The queued accounts are sent to the ProfileMetricsService, each with\n * a proof of ownership when one can be produced (see {@link #attachProofs}),\n * and the sync queue is cleared. This operation is mutexed to prevent\n * concurrent executions.\n *\n * @returns A promise that resolves when the poll is complete.\n */\n async _executePoll(): Promise<void> {\n await this.#mutex.runExclusive(async () => {\n if (!this.#assertUserOptedIn()) {\n return;\n }\n this.#setInitialDelayEndTimestampIfNull();\n if (!this.#isInitialDelayComplete()) {\n return;\n }\n const fullAccountsByAddress = this.#getFullAccountsByAddress();\n for (const [entropySourceId, accounts] of Object.entries(\n this.state.syncQueue,\n )) {\n const normalizedEntropySourceId =\n entropySourceId === 'null' ? null : entropySourceId;\n const accountsWithProofs = await this.#attachProofs(\n accounts,\n fullAccountsByAddress,\n normalizedEntropySourceId,\n );\n try {\n await this.messenger.call('ProfileMetricsService:submitMetrics', {\n metametricsId: this.#getMetaMetricsId(),\n entropySourceId: normalizedEntropySourceId,\n accounts: accountsWithProofs,\n });\n this.update((state) => {\n delete state.syncQueue[entropySourceId];\n });\n } catch (error) {\n // We want to log the error but continue processing other batches.\n console.error(\n `Failed to submit profile metrics for entropy source ID ${entropySourceId}:`,\n error,\n );\n }\n }\n });\n }\n\n /**\n * Attach a proof of ownership to each account in a single entropy-source\n * batch when possible, canonicalizing the address along the way.\n *\n * Per-account failures (unknown namespace, snap missing the\n * `signProofOfOwnership` method, snap rejection) and whole-batch nonce\n * failures are caught and downgraded to \"submit without a proof\" so the\n * batch still goes through and the proof is retried on the next poll.\n *\n * @param accounts - The queued accounts for a single batch.\n * @param fullAccountsByAddress - Live `InternalAccount` lookup keyed by address.\n * @param entropySourceId - The entropy source ID for this batch.\n * @returns The accounts with `proof` populated where signing succeeded.\n */\n async #attachProofs(\n accounts: AccountWithScopes[],\n fullAccountsByAddress: Map<string, InternalAccount>,\n entropySourceId: string | null,\n ): Promise<AccountWithScopes[]> {\n const candidates = new Map<\n string,\n { account: InternalAccount; canonicalAddress: string }\n >();\n const identifiers = new Set<string>();\n for (const queued of accounts) {\n const fullAccount = fullAccountsByAddress.get(queued.address);\n if (!fullAccount) {\n continue;\n }\n try {\n const [scope] = fullAccount.scopes;\n if (!scope) {\n throw new Error(`Scope not found for account ${fullAccount.id}`);\n }\n const { namespace } = parseCaipChainId(scope);\n const canonicalAddress = canonicalizeAddress(\n fullAccount.address,\n namespace,\n );\n candidates.set(queued.address, {\n account: fullAccount,\n canonicalAddress,\n });\n identifiers.add(canonicalAddress);\n } catch (error) {\n // Unsupported namespaces are an expected pass-through; anything\n // else is logged so a new namespace doesn't go unnoticed.\n if (!(error instanceof ProofUnsupportedNamespaceError)) {\n console.error(`Skipping proof for account ${fullAccount.id}:`, error);\n }\n }\n }\n\n if (candidates.size === 0) {\n return accounts;\n }\n\n let nonces: Record<string, string> = {};\n try {\n nonces = await this.messenger.call('ProfileMetricsService:fetchNonces', {\n identifiers: [...identifiers],\n entropySourceId,\n });\n } catch (error) {\n console.error(\n `Failed to fetch proof-of-ownership nonces for entropy source ID ${entropySourceId ?? 'null'}:`,\n error,\n );\n }\n\n return await Promise.all(\n accounts.map(async (queued): Promise<AccountWithScopes> => {\n const candidate = candidates.get(queued.address);\n if (!candidate) {\n return queued;\n }\n const nonce = nonces[candidate.canonicalAddress];\n if (!nonce) {\n return { ...queued, address: candidate.canonicalAddress };\n }\n let proof: AccountOwnershipProof;\n try {\n proof = await this.messenger.call('ProofOfOwnershipService:sign', {\n account: candidate.account,\n nonce,\n });\n } catch (error) {\n console.error(\n `Failed to sign proof of ownership for account ${candidate.account.id}:`,\n error,\n );\n return { ...queued, address: candidate.canonicalAddress };\n }\n return {\n address: candidate.canonicalAddress,\n scopes: queued.scopes,\n proof,\n };\n }),\n );\n }\n\n /**\n * Snapshot the live `InternalAccount` map keyed by address for the\n * current poll.\n *\n * @returns A map of address → `InternalAccount`.\n */\n #getFullAccountsByAddress(): Map<string, InternalAccount> {\n const byAddress = new Map<string, InternalAccount>();\n const accountsState = this.messenger.call('AccountsController:getState');\n for (const account of Object.values(\n accountsState.internalAccounts.accounts,\n )) {\n byAddress.set(account.address, account);\n }\n return byAddress;\n }\n\n /**\n * Enqueue all currently-known accounts onto the sync queue if needed.\n * Single entry point covering both the fresh-install first sync and\n * the one-time proof-of-ownership backfill for users upgrading.\n *\n * Bails for opted-out users (the poll wouldn't drain the queue\n * anyway), and bails once both bootstrap steps have already run.\n * Otherwise enqueues all known accounts and flips both flags so this\n * becomes a permanent no-op for the lifetime of the install.\n */\n async #enqueueAccountsIfNeeded(): Promise<void> {\n await this.#mutex.runExclusive(async () => {\n if (!this.#assertUserOptedIn()) {\n return;\n }\n if (\n this.state.initialEnqueueCompleted &&\n this.state.proofBackfillEnqueued\n ) {\n return;\n }\n const groupedAccounts = groupAccountsByEntropySourceId(\n Object.values(\n this.messenger.call('AccountsController:getState').internalAccounts\n .accounts,\n ),\n );\n this.update((state) => {\n // Replace the queue rather than append. `AccountsController` is\n // the source of truth and the queue is otherwise kept in sync\n // with it via the `accountAdded` / `accountRemoved` subscriptions,\n // so assigning here avoids duplicating entries that survived from\n // a prior session or were pushed earlier in this same unlock\n // cycle. Duplicates would matter because nonces are single-use:\n // letting one through causes `#attachProofs` to sign and submit\n // twice with the same nonce.\n state.syncQueue = groupedAccounts;\n state.initialEnqueueCompleted = true;\n state.proofBackfillEnqueued = true;\n });\n });\n }\n\n /**\n * Set the initial delay end timestamp if it is not already set.\n */\n #setInitialDelayEndTimestampIfNull(): void {\n this.update((state) => {\n state.initialDelayEndTimestamp ??=\n Date.now() + this.#initialDelayDuration;\n });\n }\n\n /**\n * Check if the initial delay end timestamp is in the past.\n *\n * @returns True if the initial delay period has completed, false otherwise.\n */\n #isInitialDelayComplete(): boolean {\n return (\n this.state.initialDelayEndTimestamp !== undefined &&\n Date.now() >= this.state.initialDelayEndTimestamp\n );\n }\n\n /**\n * Queue the given account to be synced at the next poll.\n *\n * @param account - The account to sync.\n */\n async #addAccountToQueue(account: InternalAccount): Promise<void> {\n await this.#mutex.runExclusive(async () => {\n this.update((state) => {\n const entropySourceId = getAccountEntropySourceId(account) ?? 'null';\n if (!state.syncQueue[entropySourceId]) {\n state.syncQueue[entropySourceId] = [];\n }\n state.syncQueue[entropySourceId].push({\n address: account.address,\n scopes: account.scopes,\n });\n });\n });\n }\n\n /**\n * Remove the given account from the sync queue.\n *\n * @param account - The account address to remove.\n */\n async #removeAccountFromQueue(account: string): Promise<void> {\n await this.#mutex.runExclusive(async () => {\n this.update((state) => {\n for (const [entropySourceId, groupedAddresses] of Object.entries(\n state.syncQueue,\n )) {\n const index = groupedAddresses.findIndex(\n ({ address }) => address === account,\n );\n if (index === -1) {\n continue;\n }\n groupedAddresses.splice(index, 1);\n if (groupedAddresses.length === 0) {\n delete state.syncQueue[entropySourceId];\n }\n break;\n }\n });\n });\n }\n}\n\n/**\n * Retrieves the entropy source ID from the given account, if it exists.\n *\n * @param account - The account from which to retrieve the entropy source ID.\n * @returns The entropy source ID, or null if it does not exist.\n */\nfunction getAccountEntropySourceId(account: InternalAccount): string | null {\n if (account.options.entropy?.type === 'mnemonic') {\n return account.options.entropy.id;\n }\n return null;\n}\n\n/**\n * Groups accounts by their entropy source ID.\n *\n * @param accounts - The accounts to group.\n * @returns An object where each key is an entropy source ID and each value is\n * an array of account addresses associated with that entropy source ID.\n */\nfunction groupAccountsByEntropySourceId(\n accounts: InternalAccount[],\n): Record<string, AccountWithScopes[]> {\n return accounts.reduce(\n (result: Record<string, AccountWithScopes[]>, account) => {\n const entropySourceId = getAccountEntropySourceId(account);\n const key = entropySourceId ?? 'null';\n if (!result[key]) {\n result[key] = [];\n }\n result[key].push({ address: account.address, scopes: account.scopes });\n return result;\n },\n {},\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ProfileMetricsController.mjs","sourceRoot":"","sources":["../src/ProfileMetricsController.ts"],"names":[],"mappings":";;;;;;;;;;;;AAgBA,OAAO,EAAE,+BAA+B,EAAE,qCAAqC;AAE/E,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,gBAAgB,EAAE,wBAAwB;AAC7E,OAAO,EAAE,KAAK,EAAE,oBAAoB;AASpC,OAAO,EACL,mBAAmB,EACnB,8BAA8B,EAC/B,iCAA6B;AAE9B;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,0BAA0B,CAAC;AAEzD;;GAEG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,cAAc,CAC1D,CAAC,EACD,QAAQ,CAAC,MAAM,CAChB,CAAC;AAgCF;;GAEG;AACH,MAAM,gCAAgC,GAAG;IACvC,uBAAuB,EAAE;QACvB,OAAO,EAAE,IAAI;QACb,sBAAsB,EAAE,IAAI;QAC5B,kBAAkB,EAAE,IAAI;QACxB,QAAQ,EAAE,KAAK;KAChB;IACD,SAAS,EAAE;QACT,OAAO,EAAE,IAAI;QACb,sBAAsB,EAAE,KAAK;QAC7B,kBAAkB,EAAE,IAAI;QACxB,QAAQ,EAAE,KAAK;KAChB;IACD,wBAAwB,EAAE;QACxB,OAAO,EAAE,IAAI;QACb,sBAAsB,EAAE,IAAI;QAC5B,kBAAkB,EAAE,IAAI;QACxB,QAAQ,EAAE,KAAK;KAChB;IACD,qBAAqB,EAAE;QACrB,OAAO,EAAE,IAAI;QACb,sBAAsB,EAAE,IAAI;QAC5B,kBAAkB,EAAE,IAAI;QACxB,QAAQ,EAAE,KAAK;KAChB;CACqD,CAAC;AAEzD;;;;;;;GAOG;AACH,MAAM,UAAU,uCAAuC;IACrD,OAAO;QACL,uBAAuB,EAAE,KAAK;QAC9B,SAAS,EAAE,EAAE;QACb,qBAAqB,EAAE,KAAK;KAC7B,CAAC;AACJ,CAAC;AAED,MAAM,yBAAyB,GAAG,CAAC,kBAAkB,CAAU,CAAC;AA6DhE;;;;;GAKG;AACH,MAAM,OAAO,wBAAyB,SAAQ,+BAA+B,EAI5E;IASC;;;;;;;;;;;;;;;;OAgBG;IACH,YAAY,EACV,SAAS,EACT,KAAK,EACL,iBAAiB,EACjB,gBAAgB,EAChB,QAAQ,GAAG,EAAE,GAAG,IAAI,EACpB,oBAAoB,GAAG,8BAA8B,GAQtD;QACC,KAAK,CAAC;YACJ,SAAS;YACT,QAAQ,EAAE,gCAAgC;YAC1C,IAAI,EAAE,cAAc;YACpB,KAAK,EAAE;gBACL,GAAG,uCAAuC,EAAE;gBAC5C,GAAG,KAAK;aACT;SACF,CAAC,CAAC;;QAhDI,0CAAS,IAAI,KAAK,EAAE,EAAC;QAErB,8DAAkC;QAElC,6DAAgC;QAEhC,iEAA8B;QA4CrC,uBAAA,IAAI,+CAAsB,iBAAiB,MAAA,CAAC;QAC5C,uBAAA,IAAI,8CAAqB,gBAAgB,MAAA,CAAC;QAC1C,uBAAA,IAAI,kDAAyB,oBAAoB,MAAA,CAAC;QAElD,IAAI,CAAC,SAAS,CAAC,4BAA4B,CACzC,IAAI,EACJ,yBAAyB,CAC1B,CAAC;QAEF,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,0BAA0B,EAAE,GAAG,EAAE;YACxD,IAAI,uBAAA,IAAI,mDAAmB,MAAvB,IAAI,CAAqB,EAAE,CAAC;gBAC9B,gEAAgE;gBAChE,yEAAyE;gBACzE,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC1B,CAAC;YACD,uBAAA,IAAI,8FAAyB,MAA7B,IAAI,CAA2B,CAAC,KAAK,CACnC,IAAI,CAAC,SAAS,CAAC,gBAAgB,IAAI,OAAO,CAAC,KAAK,CACjD,CAAC;YACF,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,wBAAwB,EAAE,GAAG,EAAE,CACtD,IAAI,CAAC,cAAc,EAAE,CACtB,CAAC;QAEF,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,4CAA4C,EAAE,GAAG,EAAE,CAC1E,IAAI,CAAC,gBAAgB,EAAE,CACxB,CAAC;QAEF,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,iCAAiC,EAAE,CAAC,OAAO,EAAE,EAAE;YACtE,uBAAA,IAAI,wFAAmB,MAAvB,IAAI,EAAoB,OAAO,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACxD,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,mCAAmC,EAAE,CAAC,OAAO,EAAE,EAAE;YACxE,uBAAA,IAAI,6FAAwB,MAA5B,IAAI,EAAyB,OAAO,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC7D,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC;IAED;;;OAGG;IACH,gBAAgB;QACd,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,KAAK,CAAC,wBAAwB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC9C,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,YAAY;QAChB,MAAM,uBAAA,IAAI,uCAAO,CAAC,YAAY,CAAC,KAAK,IAAI,EAAE;YACxC,IAAI,CAAC,uBAAA,IAAI,mDAAmB,MAAvB,IAAI,CAAqB,EAAE,CAAC;gBAC/B,OAAO;YACT,CAAC;YACD,uBAAA,IAAI,wGAAmC,MAAvC,IAAI,CAAqC,CAAC;YAC1C,IAAI,CAAC,uBAAA,IAAI,6FAAwB,MAA5B,IAAI,CAA0B,EAAE,CAAC;gBACpC,OAAO;YACT,CAAC;YACD,MAAM,qBAAqB,GAAG,uBAAA,IAAI,+FAA0B,MAA9B,IAAI,CAA4B,CAAC;YAC/D,KAAK,MAAM,CAAC,eAAe,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CACtD,IAAI,CAAC,KAAK,CAAC,SAAS,CACrB,EAAE,CAAC;gBACF,MAAM,yBAAyB,GAC7B,eAAe,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC;gBACtD,iEAAiE;gBACjE,MAAM,kBAAkB,GACtB,yBAAyB,KAAK,IAAI;oBAChC,CAAC,CAAC,QAAQ;oBACV,CAAC,CAAC,MAAM,uBAAA,IAAI,mFAAc,MAAlB,IAAI,EACR,QAAQ,EACR,qBAAqB,EACrB,yBAAyB,CAC1B,CAAC;gBACR,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,qCAAqC,EAAE;wBAC/D,aAAa,EAAE,uBAAA,IAAI,kDAAkB,MAAtB,IAAI,CAAoB;wBACvC,eAAe,EAAE,yBAAyB;wBAC1C,QAAQ,EAAE,kBAAkB;qBAC7B,CAAC,CAAC;oBACH,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;wBACpB,OAAO,KAAK,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;oBAC1C,CAAC,CAAC,CAAC;gBACL,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,kEAAkE;oBAClE,OAAO,CAAC,KAAK,CACX,0DAA0D,eAAe,GAAG,EAC5E,KAAK,CACN,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;CA0OF;;AAxOC;;;;;;;;;;;;;;;GAeG;AACH,KAAK,iDACH,QAA6B,EAC7B,qBAAmD,EACnD,eAAuB;IAEvB,MAAM,UAAU,GAAG,IAAI,GAAG,EAGvB,CAAC;IACJ,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;IACtC,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE,CAAC;QAC9B,MAAM,WAAW,GAAG,qBAAqB,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC9D,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,SAAS;QACX,CAAC;QACD,IAAI,CAAC;YACH,MAAM,CAAC,KAAK,CAAC,GAAG,WAAW,CAAC,MAAM,CAAC;YACnC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,IAAI,KAAK,CAAC,+BAA+B,WAAW,CAAC,EAAE,EAAE,CAAC,CAAC;YACnE,CAAC;YACD,MAAM,EAAE,SAAS,EAAE,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;YAC9C,MAAM,gBAAgB,GAAG,mBAAmB,CAC1C,WAAW,CAAC,OAAO,EACnB,SAAS,CACV,CAAC;YACF,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE;gBAC7B,OAAO,EAAE,WAAW;gBACpB,gBAAgB;aACjB,CAAC,CAAC;YACH,WAAW,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QACpC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,gEAAgE;YAChE,0DAA0D;YAC1D,IAAI,CAAC,CAAC,KAAK,YAAY,8BAA8B,CAAC,EAAE,CAAC;gBACvD,OAAO,CAAC,KAAK,CAAC,8BAA8B,WAAW,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;YACxE,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,UAAU,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,IAAI,MAAM,GAA2B,EAAE,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,mCAAmC,EAAE;YACtE,WAAW,EAAE,CAAC,GAAG,WAAW,CAAC;YAC7B,eAAe;SAChB,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CACX,mEAAmE,eAAe,GAAG,EACrF,KAAK,CACN,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,OAAO,CAAC,GAAG,CACtB,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,EAA8B,EAAE;QACxD,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACjD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QACjD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,SAAS,CAAC,gBAAgB,EAAE,CAAC;QAC5D,CAAC;QACD,IAAI,KAA4B,CAAC;QACjC,IAAI,CAAC;YACH,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,8BAA8B,EAAE;gBAChE,OAAO,EAAE,SAAS,CAAC,OAAO;gBAC1B,KAAK;aACN,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CACX,iDAAiD,SAAS,CAAC,OAAO,CAAC,EAAE,GAAG,EACxE,KAAK,CACN,CAAC;YACF,OAAO,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,SAAS,CAAC,gBAAgB,EAAE,CAAC;QAC5D,CAAC;QACD,OAAO;YACL,OAAO,EAAE,SAAS,CAAC,gBAAgB;YACnC,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,KAAK;SACN,CAAC;IACJ,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;IASC,MAAM,SAAS,GAAG,IAAI,GAAG,EAA2B,CAAC;IACrD,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;IACzE,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,MAAM,CACjC,aAAa,CAAC,gBAAgB,CAAC,QAAQ,CACxC,EAAE,CAAC;QACF,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;;;GASG;AACH,KAAK;IACH,MAAM,uBAAA,IAAI,uCAAO,CAAC,YAAY,CAAC,KAAK,IAAI,EAAE;QACxC,IAAI,CAAC,uBAAA,IAAI,mDAAmB,MAAvB,IAAI,CAAqB,EAAE,CAAC;YAC/B,OAAO;QACT,CAAC;QACD,IACE,IAAI,CAAC,KAAK,CAAC,uBAAuB;YAClC,IAAI,CAAC,KAAK,CAAC,qBAAqB,EAChC,CAAC;YACD,OAAO;QACT,CAAC;QACD,MAAM,eAAe,GAAG,8BAA8B,CACpD,MAAM,CAAC,MAAM,CACX,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC,gBAAgB;aAChE,QAAQ,CACZ,CACF,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,gEAAgE;YAChE,8DAA8D;YAC9D,mEAAmE;YACnE,kEAAkE;YAClE,6DAA6D;YAC7D,gEAAgE;YAChE,gEAAgE;YAChE,6BAA6B;YAC7B,KAAK,CAAC,SAAS,GAAG,eAAe,CAAC;YAClC,KAAK,CAAC,uBAAuB,GAAG,IAAI,CAAC;YACrC,KAAK,CAAC,qBAAqB,GAAG,IAAI,CAAC;QACrC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;IAMC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QACpB,KAAK,CAAC,wBAAwB,KAA9B,KAAK,CAAC,wBAAwB,GAC5B,IAAI,CAAC,GAAG,EAAE,GAAG,uBAAA,IAAI,sDAAsB,EAAC;IAC5C,CAAC,CAAC,CAAC;AACL,CAAC;IAQC,OAAO,CACL,IAAI,CAAC,KAAK,CAAC,wBAAwB,KAAK,SAAS;QACjD,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,wBAAwB,CAClD,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,KAAK,sDAAoB,OAAwB;IAC/C,MAAM,uBAAA,IAAI,uCAAO,CAAC,YAAY,CAAC,KAAK,IAAI,EAAE;QACxC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,MAAM,eAAe,GAAG,yBAAyB,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC;YACrE,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,eAAe,CAAC,EAAE,CAAC;gBACtC,KAAK,CAAC,SAAS,CAAC,eAAe,CAAC,GAAG,EAAE,CAAC;YACxC,CAAC;YACD,KAAK,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC;gBACpC,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,MAAM,EAAE,OAAO,CAAC,MAAM;aACvB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,KAAK,2DAAyB,OAAe;IAC3C,MAAM,uBAAA,IAAI,uCAAO,CAAC,YAAY,CAAC,KAAK,IAAI,EAAE;QACxC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,KAAK,MAAM,CAAC,eAAe,EAAE,gBAAgB,CAAC,IAAI,MAAM,CAAC,OAAO,CAC9D,KAAK,CAAC,SAAS,CAChB,EAAE,CAAC;gBACF,MAAM,KAAK,GAAG,gBAAgB,CAAC,SAAS,CACtC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,OAAO,KAAK,OAAO,CACrC,CAAC;gBACF,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;oBACjB,SAAS;gBACX,CAAC;gBACD,gBAAgB,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;gBAClC,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBAClC,OAAO,KAAK,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;gBAC1C,CAAC;gBACD,MAAM;YACR,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAGH;;;;;GAKG;AACH,SAAS,yBAAyB,CAAC,OAAwB;IACzD,IAAI,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,KAAK,UAAU,EAAE,CAAC;QACjD,OAAO,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;IACpC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,SAAS,8BAA8B,CACrC,QAA2B;IAE3B,OAAO,QAAQ,CAAC,MAAM,CACpB,CAAC,MAA2C,EAAE,OAAO,EAAE,EAAE;QACvD,MAAM,eAAe,GAAG,yBAAyB,CAAC,OAAO,CAAC,CAAC;QAC3D,MAAM,GAAG,GAAG,eAAe,IAAI,MAAM,CAAC;QACtC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;YACjB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;QACnB,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACvE,OAAO,MAAM,CAAC;IAChB,CAAC,EACD,EAAE,CACH,CAAC;AACJ,CAAC","sourcesContent":["import type {\n AccountsControllerAccountAddedEvent,\n AccountsControllerAccountRemovedEvent,\n AccountsControllerGetStateAction,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport type {\n KeyringControllerLockEvent,\n KeyringControllerUnlockEvent,\n} from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { Messenger } from '@metamask/messenger';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport { TransactionControllerTransactionSubmittedEvent } from '@metamask/transaction-controller';\nimport { Duration, inMilliseconds, parseCaipChainId } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\n\nimport type { ProfileMetricsControllerMethodActions } from './ProfileMetricsController-method-action-types';\nimport type {\n AccountOwnershipProof,\n AccountWithScopes,\n} from './ProfileMetricsService';\nimport type { ProfileMetricsServiceMethodActions } from './ProfileMetricsService-method-action-types';\nimport type { ProofOfOwnershipServiceMethodActions } from './ProofOfOwnershipService-method-action-types';\nimport {\n canonicalizeAddress,\n ProofUnsupportedNamespaceError,\n} from './utils/canonicalize';\n\n/**\n * The name of the {@link ProfileMetricsController}, used to namespace the\n * controller's actions and events and to namespace the controller's state data\n * when composed with other controllers.\n */\nexport const controllerName = 'ProfileMetricsController';\n\n/**\n * The default delay duration before data is sent for the first time.\n */\nexport const DEFAULT_INITIAL_DELAY_DURATION = inMilliseconds(\n 1,\n Duration.Minute,\n);\n\n/**\n * Describes the shape of the state object for {@link ProfileMetricsController}.\n */\nexport type ProfileMetricsControllerState = {\n /**\n * Whether existing accounts have been added\n * to the queue.\n */\n initialEnqueueCompleted: boolean;\n /**\n * The queue of accounts to be synced.\n * Each key is an entropy source ID, and each value is an array of account\n * addresses associated with that entropy source. Accounts with no entropy\n * source ID are grouped under the key \"null\".\n */\n syncQueue: Record<string, AccountWithScopes[]>;\n /**\n * The timestamp when the first data sending can be attempted.\n */\n initialDelayEndTimestamp?: number;\n /**\n * Whether previously-synced accounts have been re-enqueued so their\n * proofs of ownership are submitted alongside everything else. Set on\n * the first unlock after upgrading to a version that signs proofs of\n * ownership; fresh installs flip this on their initial sync since the\n * first poll already attaches proofs.\n */\n proofBackfillEnqueued: boolean;\n};\n\n/**\n * The metadata for each property in {@link ProfileMetricsControllerState}.\n */\nconst profileMetricsControllerMetadata = {\n initialEnqueueCompleted: {\n persist: true,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: false,\n },\n syncQueue: {\n persist: true,\n includeInDebugSnapshot: false,\n includeInStateLogs: true,\n usedInUi: false,\n },\n initialDelayEndTimestamp: {\n persist: true,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: false,\n },\n proofBackfillEnqueued: {\n persist: true,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: false,\n },\n} satisfies StateMetadata<ProfileMetricsControllerState>;\n\n/**\n * Constructs the default {@link ProfileMetricsController} state. This allows\n * consumers to provide a partial state object when initializing the controller\n * and also helps in constructing complete state objects for this controller in\n * tests.\n *\n * @returns The default {@link ProfileMetricsController} state.\n */\nexport function getDefaultProfileMetricsControllerState(): ProfileMetricsControllerState {\n return {\n initialEnqueueCompleted: false,\n syncQueue: {},\n proofBackfillEnqueued: false,\n };\n}\n\nconst MESSENGER_EXPOSED_METHODS = ['skipInitialDelay'] as const;\n\n/**\n * Retrieves the state of the {@link ProfileMetricsController}.\n */\nexport type ProfileMetricsControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n ProfileMetricsControllerState\n>;\n\n/**\n * Actions that {@link ProfileMetricsControllerMessenger} exposes to other consumers.\n */\nexport type ProfileMetricsControllerActions =\n | ProfileMetricsControllerGetStateAction\n | ProfileMetricsControllerMethodActions;\n\n/**\n * Actions from other messengers that {@link ProfileMetricsControllerMessenger} calls.\n */\ntype AllowedActions =\n | ProfileMetricsServiceMethodActions\n | ProofOfOwnershipServiceMethodActions\n | AccountsControllerGetStateAction;\n\n/**\n * Published when the state of {@link ProfileMetricsController} changes.\n */\nexport type ProfileMetricsControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n ProfileMetricsControllerState\n >;\n\n/**\n * Events that {@link ProfileMetricsControllerMessenger} exposes to other consumers.\n */\nexport type ProfileMetricsControllerEvents =\n ProfileMetricsControllerStateChangeEvent;\n\n/**\n * Events from other messengers that {@link ProfileMetricsControllerMessenger} subscribes\n * to.\n */\ntype AllowedEvents =\n | KeyringControllerUnlockEvent\n | KeyringControllerLockEvent\n | AccountsControllerAccountAddedEvent\n | AccountsControllerAccountRemovedEvent\n | TransactionControllerTransactionSubmittedEvent;\n\n/**\n * The messenger restricted to actions and events accessed by\n * {@link ProfileMetricsController}.\n */\nexport type ProfileMetricsControllerMessenger = Messenger<\n typeof controllerName,\n ProfileMetricsControllerActions | AllowedActions,\n ProfileMetricsControllerEvents | AllowedEvents\n>;\n\n/**\n * Manages user profile metrics.\n *\n * For users who opt-in to metrics, this controller ensures we have metrics about their user\n * profile (metrics ID and accounts).\n */\nexport class ProfileMetricsController extends StaticIntervalPollingController()<\n typeof controllerName,\n ProfileMetricsControllerState,\n ProfileMetricsControllerMessenger\n> {\n readonly #mutex = new Mutex();\n\n readonly #assertUserOptedIn: () => boolean;\n\n readonly #getMetaMetricsId: () => string;\n\n readonly #initialDelayDuration: number;\n\n /**\n * Constructs a new {@link ProfileMetricsController}.\n *\n * @param args - The constructor arguments.\n * @param args.messenger - The messenger suited for this controller.\n * @param args.state - The desired state with which to initialize this\n * controller. Missing properties will be filled in with defaults.\n * @param args.assertUserOptedIn - A function that asserts whether the user has\n * opted in to user profile features. If the user has not opted in, sync\n * operations will be no-ops.\n * @param args.getMetaMetricsId - A function that returns the MetaMetrics ID\n * of the user.\n * @param args.interval - The interval, in milliseconds, at which the controller will\n * attempt to send user profile data. Defaults to 10 seconds.\n * @param args.initialDelayDuration - The delay duration before data is sent\n * for the first time, in milliseconds. Defaults to 10 minutes.\n */\n constructor({\n messenger,\n state,\n assertUserOptedIn,\n getMetaMetricsId,\n interval = 10 * 1000,\n initialDelayDuration = DEFAULT_INITIAL_DELAY_DURATION,\n }: {\n messenger: ProfileMetricsControllerMessenger;\n state?: Partial<ProfileMetricsControllerState>;\n interval?: number;\n assertUserOptedIn: () => boolean;\n getMetaMetricsId: () => string;\n initialDelayDuration?: number;\n }) {\n super({\n messenger,\n metadata: profileMetricsControllerMetadata,\n name: controllerName,\n state: {\n ...getDefaultProfileMetricsControllerState(),\n ...state,\n },\n });\n\n this.#assertUserOptedIn = assertUserOptedIn;\n this.#getMetaMetricsId = getMetaMetricsId;\n this.#initialDelayDuration = initialDelayDuration;\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n\n this.messenger.subscribe('KeyringController:unlock', () => {\n if (this.#assertUserOptedIn()) {\n // If the user has already opted in at the start of the session,\n // it must have opted in during onboarding, or during a previous session.\n this.skipInitialDelay();\n }\n this.#enqueueAccountsIfNeeded().catch(\n this.messenger.captureException ?? console.error,\n );\n this.startPolling(null);\n });\n\n this.messenger.subscribe('KeyringController:lock', () =>\n this.stopAllPolling(),\n );\n\n this.messenger.subscribe('TransactionController:transactionSubmitted', () =>\n this.skipInitialDelay(),\n );\n\n this.messenger.subscribe('AccountsController:accountAdded', (account) => {\n this.#addAccountToQueue(account).catch(console.error);\n });\n\n this.messenger.subscribe('AccountsController:accountRemoved', (account) => {\n this.#removeAccountFromQueue(account).catch(console.error);\n });\n\n this.setIntervalLength(interval);\n }\n\n /**\n * Skip the initial delay period by setting the end timestamp to the current time.\n * Metrics will be sent on the next poll.\n */\n skipInitialDelay(): void {\n this.update((state) => {\n state.initialDelayEndTimestamp = Date.now();\n });\n }\n\n /**\n * Execute a single poll to sync user profile data.\n *\n * The queued accounts are sent to the ProfileMetricsService, each with\n * a proof of ownership when one can be produced (see {@link #attachProofs}),\n * and the sync queue is cleared. This operation is mutexed to prevent\n * concurrent executions.\n *\n * @returns A promise that resolves when the poll is complete.\n */\n async _executePoll(): Promise<void> {\n await this.#mutex.runExclusive(async () => {\n if (!this.#assertUserOptedIn()) {\n return;\n }\n this.#setInitialDelayEndTimestampIfNull();\n if (!this.#isInitialDelayComplete()) {\n return;\n }\n const fullAccountsByAddress = this.#getFullAccountsByAddress();\n for (const [entropySourceId, accounts] of Object.entries(\n this.state.syncQueue,\n )) {\n const normalizedEntropySourceId =\n entropySourceId === 'null' ? null : entropySourceId;\n // Skip proof-of-ownership for accounts without an entropy source\n const accountsWithProofs =\n normalizedEntropySourceId === null\n ? accounts\n : await this.#attachProofs(\n accounts,\n fullAccountsByAddress,\n normalizedEntropySourceId,\n );\n try {\n await this.messenger.call('ProfileMetricsService:submitMetrics', {\n metametricsId: this.#getMetaMetricsId(),\n entropySourceId: normalizedEntropySourceId,\n accounts: accountsWithProofs,\n });\n this.update((state) => {\n delete state.syncQueue[entropySourceId];\n });\n } catch (error) {\n // We want to log the error but continue processing other batches.\n console.error(\n `Failed to submit profile metrics for entropy source ID ${entropySourceId}:`,\n error,\n );\n }\n }\n });\n }\n\n /**\n * Attach a proof of ownership to each account in a single entropy-source\n * batch when possible, canonicalizing the address along the way.\n *\n * Per-account failures (unknown namespace, snap missing the\n * `signProofOfOwnership` method, snap rejection) and whole-batch nonce\n * failures are caught and downgraded to \"submit without a proof\" so the\n * batch still goes through and the proof is retried on the next poll.\n *\n * @param accounts - The queued accounts for a single batch.\n * @param fullAccountsByAddress - Live `InternalAccount` lookup keyed by address.\n * @param entropySourceId - The entropy source ID for this batch. Callers\n * are expected to short-circuit before invoking this method when the\n * batch has no entropy source; see `_executePoll` for why.\n * @returns The accounts with `proof` populated where signing succeeded.\n */\n async #attachProofs(\n accounts: AccountWithScopes[],\n fullAccountsByAddress: Map<string, InternalAccount>,\n entropySourceId: string,\n ): Promise<AccountWithScopes[]> {\n const candidates = new Map<\n string,\n { account: InternalAccount; canonicalAddress: string }\n >();\n const identifiers = new Set<string>();\n for (const queued of accounts) {\n const fullAccount = fullAccountsByAddress.get(queued.address);\n if (!fullAccount) {\n continue;\n }\n try {\n const [scope] = fullAccount.scopes;\n if (!scope) {\n throw new Error(`Scope not found for account ${fullAccount.id}`);\n }\n const { namespace } = parseCaipChainId(scope);\n const canonicalAddress = canonicalizeAddress(\n fullAccount.address,\n namespace,\n );\n candidates.set(queued.address, {\n account: fullAccount,\n canonicalAddress,\n });\n identifiers.add(canonicalAddress);\n } catch (error) {\n // Unsupported namespaces are an expected pass-through; anything\n // else is logged so a new namespace doesn't go unnoticed.\n if (!(error instanceof ProofUnsupportedNamespaceError)) {\n console.error(`Skipping proof for account ${fullAccount.id}:`, error);\n }\n }\n }\n\n if (candidates.size === 0) {\n return accounts;\n }\n\n let nonces: Record<string, string> = {};\n try {\n nonces = await this.messenger.call('ProfileMetricsService:fetchNonces', {\n identifiers: [...identifiers],\n entropySourceId,\n });\n } catch (error) {\n console.error(\n `Failed to fetch proof-of-ownership nonces for entropy source ID ${entropySourceId}:`,\n error,\n );\n }\n\n return await Promise.all(\n accounts.map(async (queued): Promise<AccountWithScopes> => {\n const candidate = candidates.get(queued.address);\n if (!candidate) {\n return queued;\n }\n const nonce = nonces[candidate.canonicalAddress];\n if (!nonce) {\n return { ...queued, address: candidate.canonicalAddress };\n }\n let proof: AccountOwnershipProof;\n try {\n proof = await this.messenger.call('ProofOfOwnershipService:sign', {\n account: candidate.account,\n nonce,\n });\n } catch (error) {\n console.error(\n `Failed to sign proof of ownership for account ${candidate.account.id}:`,\n error,\n );\n return { ...queued, address: candidate.canonicalAddress };\n }\n return {\n address: candidate.canonicalAddress,\n scopes: queued.scopes,\n proof,\n };\n }),\n );\n }\n\n /**\n * Snapshot the live `InternalAccount` map keyed by address for the\n * current poll.\n *\n * @returns A map of address → `InternalAccount`.\n */\n #getFullAccountsByAddress(): Map<string, InternalAccount> {\n const byAddress = new Map<string, InternalAccount>();\n const accountsState = this.messenger.call('AccountsController:getState');\n for (const account of Object.values(\n accountsState.internalAccounts.accounts,\n )) {\n byAddress.set(account.address, account);\n }\n return byAddress;\n }\n\n /**\n * Enqueue all currently-known accounts onto the sync queue if needed.\n * Single entry point covering both the fresh-install first sync and\n * the one-time proof-of-ownership backfill for users upgrading.\n *\n * Bails for opted-out users (the poll wouldn't drain the queue\n * anyway), and bails once both bootstrap steps have already run.\n * Otherwise enqueues all known accounts and flips both flags so this\n * becomes a permanent no-op for the lifetime of the install.\n */\n async #enqueueAccountsIfNeeded(): Promise<void> {\n await this.#mutex.runExclusive(async () => {\n if (!this.#assertUserOptedIn()) {\n return;\n }\n if (\n this.state.initialEnqueueCompleted &&\n this.state.proofBackfillEnqueued\n ) {\n return;\n }\n const groupedAccounts = groupAccountsByEntropySourceId(\n Object.values(\n this.messenger.call('AccountsController:getState').internalAccounts\n .accounts,\n ),\n );\n this.update((state) => {\n // Replace the queue rather than append. `AccountsController` is\n // the source of truth and the queue is otherwise kept in sync\n // with it via the `accountAdded` / `accountRemoved` subscriptions,\n // so assigning here avoids duplicating entries that survived from\n // a prior session or were pushed earlier in this same unlock\n // cycle. Duplicates would matter because nonces are single-use:\n // letting one through causes `#attachProofs` to sign and submit\n // twice with the same nonce.\n state.syncQueue = groupedAccounts;\n state.initialEnqueueCompleted = true;\n state.proofBackfillEnqueued = true;\n });\n });\n }\n\n /**\n * Set the initial delay end timestamp if it is not already set.\n */\n #setInitialDelayEndTimestampIfNull(): void {\n this.update((state) => {\n state.initialDelayEndTimestamp ??=\n Date.now() + this.#initialDelayDuration;\n });\n }\n\n /**\n * Check if the initial delay end timestamp is in the past.\n *\n * @returns True if the initial delay period has completed, false otherwise.\n */\n #isInitialDelayComplete(): boolean {\n return (\n this.state.initialDelayEndTimestamp !== undefined &&\n Date.now() >= this.state.initialDelayEndTimestamp\n );\n }\n\n /**\n * Queue the given account to be synced at the next poll.\n *\n * @param account - The account to sync.\n */\n async #addAccountToQueue(account: InternalAccount): Promise<void> {\n await this.#mutex.runExclusive(async () => {\n this.update((state) => {\n const entropySourceId = getAccountEntropySourceId(account) ?? 'null';\n if (!state.syncQueue[entropySourceId]) {\n state.syncQueue[entropySourceId] = [];\n }\n state.syncQueue[entropySourceId].push({\n address: account.address,\n scopes: account.scopes,\n });\n });\n });\n }\n\n /**\n * Remove the given account from the sync queue.\n *\n * @param account - The account address to remove.\n */\n async #removeAccountFromQueue(account: string): Promise<void> {\n await this.#mutex.runExclusive(async () => {\n this.update((state) => {\n for (const [entropySourceId, groupedAddresses] of Object.entries(\n state.syncQueue,\n )) {\n const index = groupedAddresses.findIndex(\n ({ address }) => address === account,\n );\n if (index === -1) {\n continue;\n }\n groupedAddresses.splice(index, 1);\n if (groupedAddresses.length === 0) {\n delete state.syncQueue[entropySourceId];\n }\n break;\n }\n });\n });\n }\n}\n\n/**\n * Retrieves the entropy source ID from the given account, if it exists.\n *\n * @param account - The account from which to retrieve the entropy source ID.\n * @returns The entropy source ID, or null if it does not exist.\n */\nfunction getAccountEntropySourceId(account: InternalAccount): string | null {\n if (account.options.entropy?.type === 'mnemonic') {\n return account.options.entropy.id;\n }\n return null;\n}\n\n/**\n * Groups accounts by their entropy source ID.\n *\n * @param accounts - The accounts to group.\n * @returns An object where each key is an entropy source ID and each value is\n * an array of account addresses associated with that entropy source ID.\n */\nfunction groupAccountsByEntropySourceId(\n accounts: InternalAccount[],\n): Record<string, AccountWithScopes[]> {\n return accounts.reduce(\n (result: Record<string, AccountWithScopes[]>, account) => {\n const entropySourceId = getAccountEntropySourceId(account);\n const key = entropySourceId ?? 'null';\n if (!result[key]) {\n result[key] = [];\n }\n result[key].push({ address: account.address, scopes: account.scopes });\n return result;\n },\n {},\n );\n}\n"]}
|