@metamask/profile-sync-controller 16.0.0 → 17.0.0

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.
Files changed (118) hide show
  1. package/CHANGELOG.md +15 -1
  2. package/dist/controllers/authentication/AuthenticationController.cjs +60 -24
  3. package/dist/controllers/authentication/AuthenticationController.cjs.map +1 -1
  4. package/dist/controllers/authentication/AuthenticationController.d.cts +6 -4
  5. package/dist/controllers/authentication/AuthenticationController.d.cts.map +1 -1
  6. package/dist/controllers/authentication/AuthenticationController.d.mts +6 -4
  7. package/dist/controllers/authentication/AuthenticationController.d.mts.map +1 -1
  8. package/dist/controllers/authentication/AuthenticationController.mjs +61 -25
  9. package/dist/controllers/authentication/AuthenticationController.mjs.map +1 -1
  10. package/dist/controllers/authentication/auth-snap-requests.cjs +25 -4
  11. package/dist/controllers/authentication/auth-snap-requests.cjs.map +1 -1
  12. package/dist/controllers/authentication/auth-snap-requests.d.cts +12 -2
  13. package/dist/controllers/authentication/auth-snap-requests.d.cts.map +1 -1
  14. package/dist/controllers/authentication/auth-snap-requests.d.mts +12 -2
  15. package/dist/controllers/authentication/auth-snap-requests.d.mts.map +1 -1
  16. package/dist/controllers/authentication/auth-snap-requests.mjs +23 -3
  17. package/dist/controllers/authentication/auth-snap-requests.mjs.map +1 -1
  18. package/dist/controllers/authentication/mocks/mockResponses.cjs +36 -3
  19. package/dist/controllers/authentication/mocks/mockResponses.cjs.map +1 -1
  20. package/dist/controllers/authentication/mocks/mockResponses.d.cts +7 -5
  21. package/dist/controllers/authentication/mocks/mockResponses.d.cts.map +1 -1
  22. package/dist/controllers/authentication/mocks/mockResponses.d.mts +7 -5
  23. package/dist/controllers/authentication/mocks/mockResponses.d.mts.map +1 -1
  24. package/dist/controllers/authentication/mocks/mockResponses.mjs +36 -3
  25. package/dist/controllers/authentication/mocks/mockResponses.mjs.map +1 -1
  26. package/dist/controllers/user-storage/UserStorageController.cjs +77 -27
  27. package/dist/controllers/user-storage/UserStorageController.cjs.map +1 -1
  28. package/dist/controllers/user-storage/UserStorageController.d.cts +24 -10
  29. package/dist/controllers/user-storage/UserStorageController.d.cts.map +1 -1
  30. package/dist/controllers/user-storage/UserStorageController.d.mts +24 -10
  31. package/dist/controllers/user-storage/UserStorageController.d.mts.map +1 -1
  32. package/dist/controllers/user-storage/UserStorageController.mjs +77 -27
  33. package/dist/controllers/user-storage/UserStorageController.mjs.map +1 -1
  34. package/dist/controllers/user-storage/account-syncing/controller-integration.cjs +22 -26
  35. package/dist/controllers/user-storage/account-syncing/controller-integration.cjs.map +1 -1
  36. package/dist/controllers/user-storage/account-syncing/controller-integration.d.cts +5 -2
  37. package/dist/controllers/user-storage/account-syncing/controller-integration.d.cts.map +1 -1
  38. package/dist/controllers/user-storage/account-syncing/controller-integration.d.mts +5 -2
  39. package/dist/controllers/user-storage/account-syncing/controller-integration.d.mts.map +1 -1
  40. package/dist/controllers/user-storage/account-syncing/controller-integration.mjs +23 -27
  41. package/dist/controllers/user-storage/account-syncing/controller-integration.mjs.map +1 -1
  42. package/dist/controllers/user-storage/account-syncing/setup-subscriptions.cjs +3 -13
  43. package/dist/controllers/user-storage/account-syncing/setup-subscriptions.cjs.map +1 -1
  44. package/dist/controllers/user-storage/account-syncing/setup-subscriptions.d.cts.map +1 -1
  45. package/dist/controllers/user-storage/account-syncing/setup-subscriptions.d.mts.map +1 -1
  46. package/dist/controllers/user-storage/account-syncing/setup-subscriptions.mjs +3 -13
  47. package/dist/controllers/user-storage/account-syncing/setup-subscriptions.mjs.map +1 -1
  48. package/dist/controllers/user-storage/account-syncing/sync-utils.cjs +16 -7
  49. package/dist/controllers/user-storage/account-syncing/sync-utils.cjs.map +1 -1
  50. package/dist/controllers/user-storage/account-syncing/sync-utils.d.cts +6 -2
  51. package/dist/controllers/user-storage/account-syncing/sync-utils.d.cts.map +1 -1
  52. package/dist/controllers/user-storage/account-syncing/sync-utils.d.mts +6 -2
  53. package/dist/controllers/user-storage/account-syncing/sync-utils.d.mts.map +1 -1
  54. package/dist/controllers/user-storage/account-syncing/sync-utils.mjs +16 -7
  55. package/dist/controllers/user-storage/account-syncing/sync-utils.mjs.map +1 -1
  56. package/dist/controllers/user-storage/account-syncing/utils.cjs +1 -42
  57. package/dist/controllers/user-storage/account-syncing/utils.cjs.map +1 -1
  58. package/dist/controllers/user-storage/account-syncing/utils.d.cts +1 -18
  59. package/dist/controllers/user-storage/account-syncing/utils.d.cts.map +1 -1
  60. package/dist/controllers/user-storage/account-syncing/utils.d.mts +1 -18
  61. package/dist/controllers/user-storage/account-syncing/utils.d.mts.map +1 -1
  62. package/dist/controllers/user-storage/account-syncing/utils.mjs +0 -39
  63. package/dist/controllers/user-storage/account-syncing/utils.mjs.map +1 -1
  64. package/dist/sdk/authentication-jwt-bearer/flow-siwe.cjs.map +1 -1
  65. package/dist/sdk/authentication-jwt-bearer/flow-siwe.d.cts.map +1 -1
  66. package/dist/sdk/authentication-jwt-bearer/flow-siwe.d.mts.map +1 -1
  67. package/dist/sdk/authentication-jwt-bearer/flow-siwe.mjs.map +1 -1
  68. package/dist/sdk/authentication-jwt-bearer/flow-srp.cjs +22 -22
  69. package/dist/sdk/authentication-jwt-bearer/flow-srp.cjs.map +1 -1
  70. package/dist/sdk/authentication-jwt-bearer/flow-srp.d.cts +4 -4
  71. package/dist/sdk/authentication-jwt-bearer/flow-srp.d.cts.map +1 -1
  72. package/dist/sdk/authentication-jwt-bearer/flow-srp.d.mts +4 -4
  73. package/dist/sdk/authentication-jwt-bearer/flow-srp.d.mts.map +1 -1
  74. package/dist/sdk/authentication-jwt-bearer/flow-srp.mjs +22 -22
  75. package/dist/sdk/authentication-jwt-bearer/flow-srp.mjs.map +1 -1
  76. package/dist/sdk/authentication-jwt-bearer/services.cjs.map +1 -1
  77. package/dist/sdk/authentication-jwt-bearer/services.d.cts.map +1 -1
  78. package/dist/sdk/authentication-jwt-bearer/services.d.mts.map +1 -1
  79. package/dist/sdk/authentication-jwt-bearer/services.mjs.map +1 -1
  80. package/dist/sdk/authentication-jwt-bearer/types.cjs.map +1 -1
  81. package/dist/sdk/authentication-jwt-bearer/types.d.cts +8 -8
  82. package/dist/sdk/authentication-jwt-bearer/types.d.cts.map +1 -1
  83. package/dist/sdk/authentication-jwt-bearer/types.d.mts +8 -8
  84. package/dist/sdk/authentication-jwt-bearer/types.d.mts.map +1 -1
  85. package/dist/sdk/authentication-jwt-bearer/types.mjs.map +1 -1
  86. package/dist/sdk/authentication.cjs +8 -11
  87. package/dist/sdk/authentication.cjs.map +1 -1
  88. package/dist/sdk/authentication.d.cts +4 -4
  89. package/dist/sdk/authentication.d.cts.map +1 -1
  90. package/dist/sdk/authentication.d.mts +4 -4
  91. package/dist/sdk/authentication.d.mts.map +1 -1
  92. package/dist/sdk/authentication.mjs +8 -11
  93. package/dist/sdk/authentication.mjs.map +1 -1
  94. package/dist/sdk/user-storage.cjs +38 -32
  95. package/dist/sdk/user-storage.cjs.map +1 -1
  96. package/dist/sdk/user-storage.d.cts +4 -3
  97. package/dist/sdk/user-storage.d.cts.map +1 -1
  98. package/dist/sdk/user-storage.d.mts +4 -3
  99. package/dist/sdk/user-storage.d.mts.map +1 -1
  100. package/dist/sdk/user-storage.mjs +38 -32
  101. package/dist/sdk/user-storage.mjs.map +1 -1
  102. package/dist/sdk/utils/messaging-signing-snap-requests.cjs +16 -4
  103. package/dist/sdk/utils/messaging-signing-snap-requests.cjs.map +1 -1
  104. package/dist/sdk/utils/messaging-signing-snap-requests.d.cts +2 -2
  105. package/dist/sdk/utils/messaging-signing-snap-requests.d.cts.map +1 -1
  106. package/dist/sdk/utils/messaging-signing-snap-requests.d.mts +2 -2
  107. package/dist/sdk/utils/messaging-signing-snap-requests.d.mts.map +1 -1
  108. package/dist/sdk/utils/messaging-signing-snap-requests.mjs +16 -4
  109. package/dist/sdk/utils/messaging-signing-snap-requests.mjs.map +1 -1
  110. package/dist/shared/storage-schema.cjs +1 -6
  111. package/dist/shared/storage-schema.cjs.map +1 -1
  112. package/dist/shared/storage-schema.d.cts +0 -1
  113. package/dist/shared/storage-schema.d.cts.map +1 -1
  114. package/dist/shared/storage-schema.d.mts +0 -1
  115. package/dist/shared/storage-schema.d.mts.map +1 -1
  116. package/dist/shared/storage-schema.mjs +0 -4
  117. package/dist/shared/storage-schema.mjs.map +1 -1
  118. package/package.json +2 -2
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.syncInternalAccountsWithUserStorage = exports.saveInternalAccountsListToUserStorage = exports.saveInternalAccountToUserStorage = void 0;
4
- const keyring_api_1 = require("@metamask/keyring-api");
5
4
  const keyring_controller_1 = require("@metamask/keyring-controller");
6
5
  const sync_utils_1 = require("./sync-utils.cjs");
7
6
  const utils_1 = require("./utils.cjs");
@@ -15,16 +14,17 @@ const storage_schema_1 = require("../../../shared/storage-schema.cjs");
15
14
  async function saveInternalAccountToUserStorage(internalAccount, options) {
16
15
  const { getUserStorageControllerInstance } = options;
17
16
  if (!(0, sync_utils_1.canPerformAccountSyncing)(options) ||
18
- !(0, keyring_api_1.isEvmAccountType)(internalAccount.type) ||
19
- !(await (0, utils_1.isInternalAccountFromPrimarySRPHdKeyring)(internalAccount, options))) {
17
+ internalAccount.metadata.keyring.type !== String(keyring_controller_1.KeyringTypes.hd) // sync only EVM accounts until we support multichain accounts
18
+ ) {
20
19
  return;
21
20
  }
21
+ // properties of `options` are (wrongly?) typed as `Json` and eslint crashes if we try to interpret it as such and call a `?.toString()` on it.
22
+ // but we know this is a string?, so we can safely cast it
23
+ const entropySourceId = internalAccount.options.entropySource;
22
24
  try {
23
25
  // Map the internal account to the user storage account schema
24
26
  const mappedAccount = (0, utils_1.mapInternalAccountToUserStorageAccount)(internalAccount);
25
- await getUserStorageControllerInstance().performSetStorage(
26
- // ESLint is confused here.
27
- `${storage_schema_1.USER_STORAGE_FEATURE_NAMES.accounts}.${internalAccount.address}`, JSON.stringify(mappedAccount));
27
+ await getUserStorageControllerInstance().performSetStorage(`${storage_schema_1.USER_STORAGE_FEATURE_NAMES.accounts}.${internalAccount.address}`, JSON.stringify(mappedAccount), entropySourceId);
28
28
  }
29
29
  catch (e) {
30
30
  // istanbul ignore next
@@ -37,10 +37,12 @@ exports.saveInternalAccountToUserStorage = saveInternalAccountToUserStorage;
37
37
  * Saves the list of internal accounts to the user storage.
38
38
  *
39
39
  * @param options - parameters used for saving the list of internal accounts
40
+ * @param entropySourceId - The entropy source ID used to derive the key,
41
+ * when multiple sources are available (Multi-SRP).
40
42
  */
41
- async function saveInternalAccountsListToUserStorage(options) {
43
+ async function saveInternalAccountsListToUserStorage(options, entropySourceId) {
42
44
  const { getUserStorageControllerInstance } = options;
43
- const internalAccountsList = await (0, sync_utils_1.getInternalAccountsList)(options);
45
+ const internalAccountsList = await (0, sync_utils_1.getInternalAccountsList)(options, entropySourceId);
44
46
  if (!internalAccountsList?.length) {
45
47
  return;
46
48
  }
@@ -48,7 +50,7 @@ async function saveInternalAccountsListToUserStorage(options) {
48
50
  await getUserStorageControllerInstance().performBatchSetStorage(storage_schema_1.USER_STORAGE_FEATURE_NAMES.accounts, internalAccountsListFormattedForUserStorage.map((account) => [
49
51
  account.a,
50
52
  JSON.stringify(account),
51
- ]));
53
+ ]), entropySourceId);
52
54
  }
53
55
  exports.saveInternalAccountsListToUserStorage = saveInternalAccountsListToUserStorage;
54
56
  /**
@@ -58,8 +60,9 @@ exports.saveInternalAccountsListToUserStorage = saveInternalAccountsListToUserSt
58
60
  *
59
61
  * @param config - parameters used for syncing the internal accounts list with the user storage accounts list
60
62
  * @param options - parameters used for syncing the internal accounts list with the user storage accounts list
63
+ * @param entropySourceId - The entropy source ID used to derive the key,
61
64
  */
62
- async function syncInternalAccountsWithUserStorage(config, options) {
65
+ async function syncInternalAccountsWithUserStorage(config, options, entropySourceId) {
63
66
  if (!(0, sync_utils_1.canPerformAccountSyncing)(options)) {
64
67
  return;
65
68
  }
@@ -67,10 +70,9 @@ async function syncInternalAccountsWithUserStorage(config, options) {
67
70
  const { getMessenger, getUserStorageControllerInstance } = options;
68
71
  try {
69
72
  await getUserStorageControllerInstance().setIsAccountSyncingInProgress(true);
70
- const userStorageAccountsList = await (0, sync_utils_1.getUserStorageAccountsList)(options);
73
+ const userStorageAccountsList = await (0, sync_utils_1.getUserStorageAccountsList)(options, entropySourceId);
71
74
  if (!userStorageAccountsList || !userStorageAccountsList.length) {
72
- await saveInternalAccountsListToUserStorage(options);
73
- await getUserStorageControllerInstance().setHasAccountSyncingSyncedAtLeastOnce(true);
75
+ await saveInternalAccountsListToUserStorage(options, entropySourceId);
74
76
  return;
75
77
  }
76
78
  // Keep a record if erroneous situations are found during the sync
@@ -80,7 +82,7 @@ async function syncInternalAccountsWithUserStorage(config, options) {
80
82
  const internalAccountsToBeSavedToUserStorage = [];
81
83
  // Compare internal accounts list with user storage accounts list
82
84
  // First step: compare lengths
83
- const internalAccountsList = await (0, sync_utils_1.getInternalAccountsList)(options);
85
+ const internalAccountsList = await (0, sync_utils_1.getInternalAccountsList)(options, entropySourceId);
84
86
  if (!internalAccountsList || !internalAccountsList.length) {
85
87
  throw new Error(`Failed to get internal accounts list`);
86
88
  }
@@ -91,11 +93,8 @@ async function syncInternalAccountsWithUserStorage(config, options) {
91
93
  const numberOfAccountsToAdd = Math.min(userStorageAccountsList.length, maxNumberOfAccountsToAdd) -
92
94
  internalAccountsList.length;
93
95
  // Create new accounts to match the user storage accounts list
94
- // NOTE: we only support the primary SRP HD keyring for now
95
- // This is why we are hardcoding the index to 0
96
96
  await getMessenger().call('KeyringController:withKeyring', {
97
- type: keyring_controller_1.KeyringTypes.hd,
98
- index: 0,
97
+ id: entropySourceId,
99
98
  }, async ({ keyring }) => {
100
99
  await keyring.addAccounts(numberOfAccountsToAdd);
101
100
  });
@@ -106,7 +105,7 @@ async function syncInternalAccountsWithUserStorage(config, options) {
106
105
  }
107
106
  // Second step: compare account names
108
107
  // Get the internal accounts list again since new accounts might have been added in the previous step
109
- const refreshedInternalAccountsList = await (0, sync_utils_1.getInternalAccountsList)(options);
108
+ const refreshedInternalAccountsList = await (0, sync_utils_1.getInternalAccountsList)(options, entropySourceId);
110
109
  const newlyAddedAccounts = refreshedInternalAccountsList.filter((account) => !internalAccountsList.find((a) => a.address === account.address));
111
110
  for (const internalAccount of refreshedInternalAccountsList) {
112
111
  const userStorageAccount = userStorageAccountsList.find((account) => account.a === internalAccount.address);
@@ -182,13 +181,13 @@ async function syncInternalAccountsWithUserStorage(config, options) {
182
181
  await getUserStorageControllerInstance().performBatchSetStorage(storage_schema_1.USER_STORAGE_FEATURE_NAMES.accounts, internalAccountsToBeSavedToUserStorage.map((account) => [
183
182
  account.address,
184
183
  JSON.stringify((0, utils_1.mapInternalAccountToUserStorageAccount)(account)),
185
- ]));
184
+ ]), entropySourceId);
186
185
  }
187
186
  // In case we have corrupted user storage with accounts that don't exist in the internal accounts list
188
187
  // Delete those accounts from the user storage
189
188
  const userStorageAccountsToBeDeleted = userStorageAccountsList.filter((account) => !refreshedInternalAccountsList.find((a) => a.address === account.a));
190
189
  if (userStorageAccountsToBeDeleted.length) {
191
- await getUserStorageControllerInstance().performBatchDeleteStorage(storage_schema_1.USER_STORAGE_FEATURE_NAMES.accounts, userStorageAccountsToBeDeleted.map((account) => account.a));
190
+ await getUserStorageControllerInstance().performBatchDeleteStorage(storage_schema_1.USER_STORAGE_FEATURE_NAMES.accounts, userStorageAccountsToBeDeleted.map((account) => account.a), entropySourceId);
192
191
  erroneousSituationsFound = true;
193
192
  onAccountSyncErroneousSituation?.('An account was present in the user storage accounts list but was not found in the internal accounts list after the sync', {
194
193
  userStorageAccountsToBeDeleted,
@@ -200,8 +199,8 @@ async function syncInternalAccountsWithUserStorage(config, options) {
200
199
  }
201
200
  if (erroneousSituationsFound) {
202
201
  const [finalUserStorageAccountsList, finalInternalAccountsList] = await Promise.all([
203
- (0, sync_utils_1.getUserStorageAccountsList)(options),
204
- (0, sync_utils_1.getInternalAccountsList)(options),
202
+ (0, sync_utils_1.getUserStorageAccountsList)(options, entropySourceId),
203
+ (0, sync_utils_1.getInternalAccountsList)(options, entropySourceId),
205
204
  ]);
206
205
  const doesEveryAccountInInternalAccountsListExistInUserStorageAccountsList = finalInternalAccountsList.every((account) => finalUserStorageAccountsList?.some((userStorageAccount) => userStorageAccount.a === account.address));
207
206
  // istanbul ignore next
@@ -221,9 +220,6 @@ async function syncInternalAccountsWithUserStorage(config, options) {
221
220
  onAccountSyncErroneousSituation?.('Erroneous situations were found during the sync, and final state does not match the expected state', context);
222
221
  }
223
222
  }
224
- // We do this here and not in the finally statement because we want to make sure that
225
- // the accounts are saved / updated / deleted at least once before we set this flag
226
- await getUserStorageControllerInstance().setHasAccountSyncingSyncedAtLeastOnce(true);
227
223
  }
228
224
  catch (e) {
229
225
  // istanbul ignore next
@@ -1 +1 @@
1
- {"version":3,"file":"controller-integration.cjs","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/controller-integration.ts"],"names":[],"mappings":"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","sourcesContent":["import { isEvmAccountType } from '@metamask/keyring-api';\nimport { KeyringTypes } from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\n\nimport {\n canPerformAccountSyncing,\n getInternalAccountsList,\n getUserStorageAccountsList,\n} from './sync-utils';\nimport type { AccountSyncingOptions } from './types';\nimport {\n isInternalAccountFromPrimarySRPHdKeyring,\n isNameDefaultAccountName,\n mapInternalAccountToUserStorageAccount,\n} from './utils';\nimport { USER_STORAGE_FEATURE_NAMES } from '../../../shared/storage-schema';\n\n/**\n * Saves an individual internal account to the user storage.\n *\n * @param internalAccount - The internal account to save\n * @param options - parameters used for saving the internal account\n */\nexport async function saveInternalAccountToUserStorage(\n internalAccount: InternalAccount,\n options: AccountSyncingOptions,\n): Promise<void> {\n const { getUserStorageControllerInstance } = options;\n\n if (\n !canPerformAccountSyncing(options) ||\n !isEvmAccountType(internalAccount.type) ||\n !(await isInternalAccountFromPrimarySRPHdKeyring(internalAccount, options))\n ) {\n return;\n }\n\n try {\n // Map the internal account to the user storage account schema\n const mappedAccount =\n mapInternalAccountToUserStorageAccount(internalAccount);\n\n await getUserStorageControllerInstance().performSetStorage(\n // ESLint is confused here.\n\n `${USER_STORAGE_FEATURE_NAMES.accounts}.${internalAccount.address}`,\n JSON.stringify(mappedAccount),\n );\n } catch (e) {\n // istanbul ignore next\n const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);\n throw new Error(\n `UserStorageController - failed to save account to user storage - ${errorMessage}`,\n );\n }\n}\n\n/**\n * Saves the list of internal accounts to the user storage.\n *\n * @param options - parameters used for saving the list of internal accounts\n */\nexport async function saveInternalAccountsListToUserStorage(\n options: AccountSyncingOptions,\n): Promise<void> {\n const { getUserStorageControllerInstance } = options;\n\n const internalAccountsList = await getInternalAccountsList(options);\n\n if (!internalAccountsList?.length) {\n return;\n }\n\n const internalAccountsListFormattedForUserStorage = internalAccountsList.map(\n mapInternalAccountToUserStorageAccount,\n );\n\n await getUserStorageControllerInstance().performBatchSetStorage(\n USER_STORAGE_FEATURE_NAMES.accounts,\n internalAccountsListFormattedForUserStorage.map((account) => [\n account.a,\n JSON.stringify(account),\n ]),\n );\n}\n\ntype SyncInternalAccountsWithUserStorageConfig = {\n maxNumberOfAccountsToAdd?: number;\n onAccountAdded?: () => void;\n onAccountNameUpdated?: () => void;\n onAccountSyncErroneousSituation?: (\n errorMessage: string,\n sentryContext?: Record<string, unknown>,\n ) => void;\n};\n\n/**\n * Syncs the internal accounts list with the user storage accounts list.\n * This method is used to make sure that the internal accounts list is up-to-date with the user storage accounts list and vice-versa.\n * It will add new accounts to the internal accounts list, update/merge conflicting names and re-upload the results in some cases to the user storage.\n *\n * @param config - parameters used for syncing the internal accounts list with the user storage accounts list\n * @param options - parameters used for syncing the internal accounts list with the user storage accounts list\n */\nexport async function syncInternalAccountsWithUserStorage(\n config: SyncInternalAccountsWithUserStorageConfig,\n options: AccountSyncingOptions,\n): Promise<void> {\n if (!canPerformAccountSyncing(options)) {\n return;\n }\n\n const {\n maxNumberOfAccountsToAdd = Infinity,\n onAccountAdded,\n onAccountNameUpdated,\n onAccountSyncErroneousSituation,\n } = config;\n const { getMessenger, getUserStorageControllerInstance } = options;\n\n try {\n await getUserStorageControllerInstance().setIsAccountSyncingInProgress(\n true,\n );\n\n const userStorageAccountsList = await getUserStorageAccountsList(options);\n\n if (!userStorageAccountsList || !userStorageAccountsList.length) {\n await saveInternalAccountsListToUserStorage(options);\n await getUserStorageControllerInstance().setHasAccountSyncingSyncedAtLeastOnce(\n true,\n );\n return;\n }\n // Keep a record if erroneous situations are found during the sync\n // This is done so we can send the context to Sentry in case of an erroneous situation\n let erroneousSituationsFound = false;\n\n // Prepare an array of internal accounts to be saved to the user storage\n const internalAccountsToBeSavedToUserStorage: InternalAccount[] = [];\n\n // Compare internal accounts list with user storage accounts list\n // First step: compare lengths\n const internalAccountsList = await getInternalAccountsList(options);\n\n if (!internalAccountsList || !internalAccountsList.length) {\n throw new Error(`Failed to get internal accounts list`);\n }\n\n const hasMoreUserStorageAccountsThanInternalAccounts =\n userStorageAccountsList.length > internalAccountsList.length;\n\n // We don't want to remove existing accounts for a user\n // so we only add new accounts if the user has more accounts in user storage than internal accounts\n if (hasMoreUserStorageAccountsThanInternalAccounts) {\n const numberOfAccountsToAdd =\n Math.min(userStorageAccountsList.length, maxNumberOfAccountsToAdd) -\n internalAccountsList.length;\n\n // Create new accounts to match the user storage accounts list\n // NOTE: we only support the primary SRP HD keyring for now\n // This is why we are hardcoding the index to 0\n await getMessenger().call(\n 'KeyringController:withKeyring',\n {\n type: KeyringTypes.hd,\n index: 0,\n },\n async ({ keyring }) => {\n await keyring.addAccounts(numberOfAccountsToAdd);\n },\n );\n\n // TODO: below code is kept for analytics but should probably be re-thought\n for (let i = 0; i < numberOfAccountsToAdd; i++) {\n onAccountAdded?.();\n }\n }\n\n // Second step: compare account names\n // Get the internal accounts list again since new accounts might have been added in the previous step\n const refreshedInternalAccountsList =\n await getInternalAccountsList(options);\n\n const newlyAddedAccounts = refreshedInternalAccountsList.filter(\n (account) =>\n !internalAccountsList.find((a) => a.address === account.address),\n );\n\n for (const internalAccount of refreshedInternalAccountsList) {\n const userStorageAccount = userStorageAccountsList.find(\n (account) => account.a === internalAccount.address,\n );\n\n // If the account is not present in user storage\n // istanbul ignore next\n if (!userStorageAccount) {\n // If the account was just added in the previous step, skip saving it, it's likely to be a bogus account\n if (newlyAddedAccounts.includes(internalAccount)) {\n erroneousSituationsFound = true;\n onAccountSyncErroneousSituation?.(\n 'An account was added to the internal accounts list but was not present in the user storage accounts list',\n {\n internalAccount,\n userStorageAccount,\n newlyAddedAccounts,\n userStorageAccountsList,\n internalAccountsList,\n refreshedInternalAccountsList,\n internalAccountsToBeSavedToUserStorage,\n },\n );\n continue;\n }\n // Otherwise, it means that this internal account was present before the sync, and needs to be saved to the user storage\n internalAccountsToBeSavedToUserStorage.push(internalAccount);\n continue;\n }\n\n // From this point on, we know that the account is present in\n // both the internal accounts list and the user storage accounts list\n\n // One or both accounts have default names\n const isInternalAccountNameDefault = isNameDefaultAccountName(\n internalAccount.metadata.name,\n );\n const isUserStorageAccountNameDefault = isNameDefaultAccountName(\n userStorageAccount.n,\n );\n\n // Internal account has default name\n if (isInternalAccountNameDefault) {\n if (!isUserStorageAccountNameDefault) {\n getMessenger().call(\n 'AccountsController:updateAccountMetadata',\n internalAccount.id,\n {\n name: userStorageAccount.n,\n },\n );\n\n onAccountNameUpdated?.();\n }\n continue;\n }\n\n // Internal account has custom name but user storage account has default name\n if (isUserStorageAccountNameDefault) {\n internalAccountsToBeSavedToUserStorage.push(internalAccount);\n continue;\n }\n\n // Both accounts have custom names\n\n // User storage account has a nameLastUpdatedAt timestamp\n // Note: not storing the undefined checks in constants to act as a type guard\n if (userStorageAccount.nlu !== undefined) {\n if (internalAccount.metadata.nameLastUpdatedAt !== undefined) {\n const isInternalAccountNameNewer =\n internalAccount.metadata.nameLastUpdatedAt > userStorageAccount.nlu;\n\n if (isInternalAccountNameNewer) {\n internalAccountsToBeSavedToUserStorage.push(internalAccount);\n continue;\n }\n }\n\n getMessenger().call(\n 'AccountsController:updateAccountMetadata',\n internalAccount.id,\n {\n name: userStorageAccount.n,\n nameLastUpdatedAt: userStorageAccount.nlu,\n },\n );\n\n const areInternalAndUserStorageAccountNamesEqual =\n internalAccount.metadata.name === userStorageAccount.n;\n\n if (!areInternalAndUserStorageAccountNamesEqual) {\n onAccountNameUpdated?.();\n }\n\n continue;\n } else if (internalAccount.metadata.nameLastUpdatedAt !== undefined) {\n internalAccountsToBeSavedToUserStorage.push(internalAccount);\n continue;\n }\n }\n\n // Save the internal accounts list to the user storage\n if (internalAccountsToBeSavedToUserStorage.length) {\n await getUserStorageControllerInstance().performBatchSetStorage(\n USER_STORAGE_FEATURE_NAMES.accounts,\n internalAccountsToBeSavedToUserStorage.map((account) => [\n account.address,\n JSON.stringify(mapInternalAccountToUserStorageAccount(account)),\n ]),\n );\n }\n\n // In case we have corrupted user storage with accounts that don't exist in the internal accounts list\n // Delete those accounts from the user storage\n const userStorageAccountsToBeDeleted = userStorageAccountsList.filter(\n (account) =>\n !refreshedInternalAccountsList.find((a) => a.address === account.a),\n );\n\n if (userStorageAccountsToBeDeleted.length) {\n await getUserStorageControllerInstance().performBatchDeleteStorage(\n USER_STORAGE_FEATURE_NAMES.accounts,\n userStorageAccountsToBeDeleted.map((account) => account.a),\n );\n erroneousSituationsFound = true;\n onAccountSyncErroneousSituation?.(\n 'An account was present in the user storage accounts list but was not found in the internal accounts list after the sync',\n {\n userStorageAccountsToBeDeleted,\n internalAccountsList,\n refreshedInternalAccountsList,\n internalAccountsToBeSavedToUserStorage,\n userStorageAccountsList,\n },\n );\n }\n\n if (erroneousSituationsFound) {\n const [finalUserStorageAccountsList, finalInternalAccountsList] =\n await Promise.all([\n getUserStorageAccountsList(options),\n getInternalAccountsList(options),\n ]);\n\n const doesEveryAccountInInternalAccountsListExistInUserStorageAccountsList =\n finalInternalAccountsList.every((account) =>\n finalUserStorageAccountsList?.some(\n (userStorageAccount) => userStorageAccount.a === account.address,\n ),\n );\n\n // istanbul ignore next\n const doesEveryAccountInUserStorageAccountsListExistInInternalAccountsList =\n (finalUserStorageAccountsList?.length || 0) > maxNumberOfAccountsToAdd\n ? true\n : finalUserStorageAccountsList?.every((account) =>\n finalInternalAccountsList.some(\n (internalAccount) => internalAccount.address === account.a,\n ),\n );\n\n const doFinalListsMatch =\n doesEveryAccountInInternalAccountsListExistInUserStorageAccountsList &&\n doesEveryAccountInUserStorageAccountsListExistInInternalAccountsList;\n\n const context = {\n finalUserStorageAccountsList,\n finalInternalAccountsList,\n };\n if (doFinalListsMatch) {\n onAccountSyncErroneousSituation?.(\n 'Erroneous situations were found during the sync, but final state matches the expected state',\n context,\n );\n } else {\n onAccountSyncErroneousSituation?.(\n 'Erroneous situations were found during the sync, and final state does not match the expected state',\n context,\n );\n }\n }\n\n // We do this here and not in the finally statement because we want to make sure that\n // the accounts are saved / updated / deleted at least once before we set this flag\n await getUserStorageControllerInstance().setHasAccountSyncingSyncedAtLeastOnce(\n true,\n );\n } catch (e) {\n // istanbul ignore next\n const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);\n throw new Error(\n `UserStorageController - failed to sync user storage accounts list - ${errorMessage}`,\n );\n } finally {\n await getUserStorageControllerInstance().setIsAccountSyncingInProgress(\n false,\n );\n }\n}\n"]}
1
+ {"version":3,"file":"controller-integration.cjs","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/controller-integration.ts"],"names":[],"mappings":"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","sourcesContent":["import { KeyringTypes } from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\n\nimport {\n canPerformAccountSyncing,\n getInternalAccountsList,\n getUserStorageAccountsList,\n} from './sync-utils';\nimport type { AccountSyncingOptions } from './types';\nimport {\n isNameDefaultAccountName,\n mapInternalAccountToUserStorageAccount,\n} from './utils';\nimport { USER_STORAGE_FEATURE_NAMES } from '../../../shared/storage-schema';\n\n/**\n * Saves an individual internal account to the user storage.\n *\n * @param internalAccount - The internal account to save\n * @param options - parameters used for saving the internal account\n */\nexport async function saveInternalAccountToUserStorage(\n internalAccount: InternalAccount,\n options: AccountSyncingOptions,\n): Promise<void> {\n const { getUserStorageControllerInstance } = options;\n\n if (\n !canPerformAccountSyncing(options) ||\n internalAccount.metadata.keyring.type !== String(KeyringTypes.hd) // sync only EVM accounts until we support multichain accounts\n ) {\n return;\n }\n\n // properties of `options` are (wrongly?) typed as `Json` and eslint crashes if we try to interpret it as such and call a `?.toString()` on it.\n // but we know this is a string?, so we can safely cast it\n const entropySourceId = internalAccount.options.entropySource as\n | string\n | undefined;\n\n try {\n // Map the internal account to the user storage account schema\n const mappedAccount =\n mapInternalAccountToUserStorageAccount(internalAccount);\n\n await getUserStorageControllerInstance().performSetStorage(\n `${USER_STORAGE_FEATURE_NAMES.accounts}.${internalAccount.address}`,\n JSON.stringify(mappedAccount),\n entropySourceId,\n );\n } catch (e) {\n // istanbul ignore next\n const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);\n throw new Error(\n `UserStorageController - failed to save account to user storage - ${errorMessage}`,\n );\n }\n}\n\n/**\n * Saves the list of internal accounts to the user storage.\n *\n * @param options - parameters used for saving the list of internal accounts\n * @param entropySourceId - The entropy source ID used to derive the key,\n * when multiple sources are available (Multi-SRP).\n */\nexport async function saveInternalAccountsListToUserStorage(\n options: AccountSyncingOptions,\n entropySourceId: string,\n): Promise<void> {\n const { getUserStorageControllerInstance } = options;\n\n const internalAccountsList = await getInternalAccountsList(\n options,\n entropySourceId,\n );\n\n if (!internalAccountsList?.length) {\n return;\n }\n\n const internalAccountsListFormattedForUserStorage = internalAccountsList.map(\n mapInternalAccountToUserStorageAccount,\n );\n\n await getUserStorageControllerInstance().performBatchSetStorage(\n USER_STORAGE_FEATURE_NAMES.accounts,\n internalAccountsListFormattedForUserStorage.map((account) => [\n account.a,\n JSON.stringify(account),\n ]),\n entropySourceId,\n );\n}\n\ntype SyncInternalAccountsWithUserStorageConfig = {\n maxNumberOfAccountsToAdd?: number;\n onAccountAdded?: () => void;\n onAccountNameUpdated?: () => void;\n onAccountSyncErroneousSituation?: (\n errorMessage: string,\n sentryContext?: Record<string, unknown>,\n ) => void;\n};\n\n/**\n * Syncs the internal accounts list with the user storage accounts list.\n * This method is used to make sure that the internal accounts list is up-to-date with the user storage accounts list and vice-versa.\n * It will add new accounts to the internal accounts list, update/merge conflicting names and re-upload the results in some cases to the user storage.\n *\n * @param config - parameters used for syncing the internal accounts list with the user storage accounts list\n * @param options - parameters used for syncing the internal accounts list with the user storage accounts list\n * @param entropySourceId - The entropy source ID used to derive the key,\n */\nexport async function syncInternalAccountsWithUserStorage(\n config: SyncInternalAccountsWithUserStorageConfig,\n options: AccountSyncingOptions,\n entropySourceId: string,\n): Promise<void> {\n if (!canPerformAccountSyncing(options)) {\n return;\n }\n\n const {\n maxNumberOfAccountsToAdd = Infinity,\n onAccountAdded,\n onAccountNameUpdated,\n onAccountSyncErroneousSituation,\n } = config;\n const { getMessenger, getUserStorageControllerInstance } = options;\n\n try {\n await getUserStorageControllerInstance().setIsAccountSyncingInProgress(\n true,\n );\n\n const userStorageAccountsList = await getUserStorageAccountsList(\n options,\n entropySourceId,\n );\n\n if (!userStorageAccountsList || !userStorageAccountsList.length) {\n await saveInternalAccountsListToUserStorage(options, entropySourceId);\n return;\n }\n // Keep a record if erroneous situations are found during the sync\n // This is done so we can send the context to Sentry in case of an erroneous situation\n let erroneousSituationsFound = false;\n\n // Prepare an array of internal accounts to be saved to the user storage\n const internalAccountsToBeSavedToUserStorage: InternalAccount[] = [];\n\n // Compare internal accounts list with user storage accounts list\n // First step: compare lengths\n const internalAccountsList = await getInternalAccountsList(\n options,\n entropySourceId,\n );\n\n if (!internalAccountsList || !internalAccountsList.length) {\n throw new Error(`Failed to get internal accounts list`);\n }\n\n const hasMoreUserStorageAccountsThanInternalAccounts =\n userStorageAccountsList.length > internalAccountsList.length;\n\n // We don't want to remove existing accounts for a user\n // so we only add new accounts if the user has more accounts in user storage than internal accounts\n if (hasMoreUserStorageAccountsThanInternalAccounts) {\n const numberOfAccountsToAdd =\n Math.min(userStorageAccountsList.length, maxNumberOfAccountsToAdd) -\n internalAccountsList.length;\n\n // Create new accounts to match the user storage accounts list\n await getMessenger().call(\n 'KeyringController:withKeyring',\n {\n id: entropySourceId,\n },\n async ({ keyring }) => {\n await keyring.addAccounts(numberOfAccountsToAdd);\n },\n );\n\n // TODO: below code is kept for analytics but should probably be re-thought\n for (let i = 0; i < numberOfAccountsToAdd; i++) {\n onAccountAdded?.();\n }\n }\n\n // Second step: compare account names\n // Get the internal accounts list again since new accounts might have been added in the previous step\n const refreshedInternalAccountsList = await getInternalAccountsList(\n options,\n entropySourceId,\n );\n\n const newlyAddedAccounts = refreshedInternalAccountsList.filter(\n (account) =>\n !internalAccountsList.find((a) => a.address === account.address),\n );\n\n for (const internalAccount of refreshedInternalAccountsList) {\n const userStorageAccount = userStorageAccountsList.find(\n (account) => account.a === internalAccount.address,\n );\n\n // If the account is not present in user storage\n // istanbul ignore next\n if (!userStorageAccount) {\n // If the account was just added in the previous step, skip saving it, it's likely to be a bogus account\n if (newlyAddedAccounts.includes(internalAccount)) {\n erroneousSituationsFound = true;\n onAccountSyncErroneousSituation?.(\n 'An account was added to the internal accounts list but was not present in the user storage accounts list',\n {\n internalAccount,\n userStorageAccount,\n newlyAddedAccounts,\n userStorageAccountsList,\n internalAccountsList,\n refreshedInternalAccountsList,\n internalAccountsToBeSavedToUserStorage,\n },\n );\n continue;\n }\n // Otherwise, it means that this internal account was present before the sync, and needs to be saved to the user storage\n internalAccountsToBeSavedToUserStorage.push(internalAccount);\n continue;\n }\n\n // From this point on, we know that the account is present in\n // both the internal accounts list and the user storage accounts list\n\n // One or both accounts have default names\n const isInternalAccountNameDefault = isNameDefaultAccountName(\n internalAccount.metadata.name,\n );\n const isUserStorageAccountNameDefault = isNameDefaultAccountName(\n userStorageAccount.n,\n );\n\n // Internal account has default name\n if (isInternalAccountNameDefault) {\n if (!isUserStorageAccountNameDefault) {\n getMessenger().call(\n 'AccountsController:updateAccountMetadata',\n internalAccount.id,\n {\n name: userStorageAccount.n,\n },\n );\n\n onAccountNameUpdated?.();\n }\n continue;\n }\n\n // Internal account has custom name but user storage account has default name\n if (isUserStorageAccountNameDefault) {\n internalAccountsToBeSavedToUserStorage.push(internalAccount);\n continue;\n }\n\n // Both accounts have custom names\n\n // User storage account has a nameLastUpdatedAt timestamp\n // Note: not storing the undefined checks in constants to act as a type guard\n if (userStorageAccount.nlu !== undefined) {\n if (internalAccount.metadata.nameLastUpdatedAt !== undefined) {\n const isInternalAccountNameNewer =\n internalAccount.metadata.nameLastUpdatedAt > userStorageAccount.nlu;\n\n if (isInternalAccountNameNewer) {\n internalAccountsToBeSavedToUserStorage.push(internalAccount);\n continue;\n }\n }\n\n getMessenger().call(\n 'AccountsController:updateAccountMetadata',\n internalAccount.id,\n {\n name: userStorageAccount.n,\n nameLastUpdatedAt: userStorageAccount.nlu,\n },\n );\n\n const areInternalAndUserStorageAccountNamesEqual =\n internalAccount.metadata.name === userStorageAccount.n;\n\n if (!areInternalAndUserStorageAccountNamesEqual) {\n onAccountNameUpdated?.();\n }\n\n continue;\n } else if (internalAccount.metadata.nameLastUpdatedAt !== undefined) {\n internalAccountsToBeSavedToUserStorage.push(internalAccount);\n continue;\n }\n }\n\n // Save the internal accounts list to the user storage\n if (internalAccountsToBeSavedToUserStorage.length) {\n await getUserStorageControllerInstance().performBatchSetStorage(\n USER_STORAGE_FEATURE_NAMES.accounts,\n internalAccountsToBeSavedToUserStorage.map((account) => [\n account.address,\n JSON.stringify(mapInternalAccountToUserStorageAccount(account)),\n ]),\n entropySourceId,\n );\n }\n\n // In case we have corrupted user storage with accounts that don't exist in the internal accounts list\n // Delete those accounts from the user storage\n const userStorageAccountsToBeDeleted = userStorageAccountsList.filter(\n (account) =>\n !refreshedInternalAccountsList.find((a) => a.address === account.a),\n );\n\n if (userStorageAccountsToBeDeleted.length) {\n await getUserStorageControllerInstance().performBatchDeleteStorage(\n USER_STORAGE_FEATURE_NAMES.accounts,\n userStorageAccountsToBeDeleted.map((account) => account.a),\n entropySourceId,\n );\n erroneousSituationsFound = true;\n onAccountSyncErroneousSituation?.(\n 'An account was present in the user storage accounts list but was not found in the internal accounts list after the sync',\n {\n userStorageAccountsToBeDeleted,\n internalAccountsList,\n refreshedInternalAccountsList,\n internalAccountsToBeSavedToUserStorage,\n userStorageAccountsList,\n },\n );\n }\n\n if (erroneousSituationsFound) {\n const [finalUserStorageAccountsList, finalInternalAccountsList] =\n await Promise.all([\n getUserStorageAccountsList(options, entropySourceId),\n getInternalAccountsList(options, entropySourceId),\n ]);\n\n const doesEveryAccountInInternalAccountsListExistInUserStorageAccountsList =\n finalInternalAccountsList.every((account) =>\n finalUserStorageAccountsList?.some(\n (userStorageAccount) => userStorageAccount.a === account.address,\n ),\n );\n\n // istanbul ignore next\n const doesEveryAccountInUserStorageAccountsListExistInInternalAccountsList =\n (finalUserStorageAccountsList?.length || 0) > maxNumberOfAccountsToAdd\n ? true\n : finalUserStorageAccountsList?.every((account) =>\n finalInternalAccountsList.some(\n (internalAccount) => internalAccount.address === account.a,\n ),\n );\n\n const doFinalListsMatch =\n doesEveryAccountInInternalAccountsListExistInUserStorageAccountsList &&\n doesEveryAccountInUserStorageAccountsListExistInInternalAccountsList;\n\n const context = {\n finalUserStorageAccountsList,\n finalInternalAccountsList,\n };\n if (doFinalListsMatch) {\n onAccountSyncErroneousSituation?.(\n 'Erroneous situations were found during the sync, but final state matches the expected state',\n context,\n );\n } else {\n onAccountSyncErroneousSituation?.(\n 'Erroneous situations were found during the sync, and final state does not match the expected state',\n context,\n );\n }\n }\n } catch (e) {\n // istanbul ignore next\n const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);\n throw new Error(\n `UserStorageController - failed to sync user storage accounts list - ${errorMessage}`,\n );\n } finally {\n await getUserStorageControllerInstance().setIsAccountSyncingInProgress(\n false,\n );\n }\n}\n"]}
@@ -11,8 +11,10 @@ export declare function saveInternalAccountToUserStorage(internalAccount: Intern
11
11
  * Saves the list of internal accounts to the user storage.
12
12
  *
13
13
  * @param options - parameters used for saving the list of internal accounts
14
+ * @param entropySourceId - The entropy source ID used to derive the key,
15
+ * when multiple sources are available (Multi-SRP).
14
16
  */
15
- export declare function saveInternalAccountsListToUserStorage(options: AccountSyncingOptions): Promise<void>;
17
+ export declare function saveInternalAccountsListToUserStorage(options: AccountSyncingOptions, entropySourceId: string): Promise<void>;
16
18
  type SyncInternalAccountsWithUserStorageConfig = {
17
19
  maxNumberOfAccountsToAdd?: number;
18
20
  onAccountAdded?: () => void;
@@ -26,7 +28,8 @@ type SyncInternalAccountsWithUserStorageConfig = {
26
28
  *
27
29
  * @param config - parameters used for syncing the internal accounts list with the user storage accounts list
28
30
  * @param options - parameters used for syncing the internal accounts list with the user storage accounts list
31
+ * @param entropySourceId - The entropy source ID used to derive the key,
29
32
  */
30
- export declare function syncInternalAccountsWithUserStorage(config: SyncInternalAccountsWithUserStorageConfig, options: AccountSyncingOptions): Promise<void>;
33
+ export declare function syncInternalAccountsWithUserStorage(config: SyncInternalAccountsWithUserStorageConfig, options: AccountSyncingOptions, entropySourceId: string): Promise<void>;
31
34
  export {};
32
35
  //# sourceMappingURL=controller-integration.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"controller-integration.d.cts","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/controller-integration.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AAOtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,oBAAgB;AAQrD;;;;;GAKG;AACH,wBAAsB,gCAAgC,CACpD,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC,CA6Bf;AAED;;;;GAIG;AACH,wBAAsB,qCAAqC,CACzD,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED,KAAK,yCAAyC,GAAG;IAC/C,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC;IAClC,+BAA+B,CAAC,EAAE,CAChC,YAAY,EAAE,MAAM,EACpB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KACpC,IAAI,CAAC;CACX,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAsB,mCAAmC,CACvD,MAAM,EAAE,yCAAyC,EACjD,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAwRf"}
1
+ {"version":3,"file":"controller-integration.d.cts","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/controller-integration.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AAOtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,oBAAgB;AAOrD;;;;;GAKG;AACH,wBAAsB,gCAAgC,CACpD,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAiCf;AAED;;;;;;GAMG;AACH,wBAAsB,qCAAqC,CACzD,OAAO,EAAE,qBAAqB,EAC9B,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,IAAI,CAAC,CAwBf;AAED,KAAK,yCAAyC,GAAG;IAC/C,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC;IAClC,+BAA+B,CAAC,EAAE,CAChC,YAAY,EAAE,MAAM,EACpB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KACpC,IAAI,CAAC;CACX,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAsB,mCAAmC,CACvD,MAAM,EAAE,yCAAyC,EACjD,OAAO,EAAE,qBAAqB,EAC9B,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,IAAI,CAAC,CAsRf"}
@@ -11,8 +11,10 @@ export declare function saveInternalAccountToUserStorage(internalAccount: Intern
11
11
  * Saves the list of internal accounts to the user storage.
12
12
  *
13
13
  * @param options - parameters used for saving the list of internal accounts
14
+ * @param entropySourceId - The entropy source ID used to derive the key,
15
+ * when multiple sources are available (Multi-SRP).
14
16
  */
15
- export declare function saveInternalAccountsListToUserStorage(options: AccountSyncingOptions): Promise<void>;
17
+ export declare function saveInternalAccountsListToUserStorage(options: AccountSyncingOptions, entropySourceId: string): Promise<void>;
16
18
  type SyncInternalAccountsWithUserStorageConfig = {
17
19
  maxNumberOfAccountsToAdd?: number;
18
20
  onAccountAdded?: () => void;
@@ -26,7 +28,8 @@ type SyncInternalAccountsWithUserStorageConfig = {
26
28
  *
27
29
  * @param config - parameters used for syncing the internal accounts list with the user storage accounts list
28
30
  * @param options - parameters used for syncing the internal accounts list with the user storage accounts list
31
+ * @param entropySourceId - The entropy source ID used to derive the key,
29
32
  */
30
- export declare function syncInternalAccountsWithUserStorage(config: SyncInternalAccountsWithUserStorageConfig, options: AccountSyncingOptions): Promise<void>;
33
+ export declare function syncInternalAccountsWithUserStorage(config: SyncInternalAccountsWithUserStorageConfig, options: AccountSyncingOptions, entropySourceId: string): Promise<void>;
31
34
  export {};
32
35
  //# sourceMappingURL=controller-integration.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"controller-integration.d.mts","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/controller-integration.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AAOtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,oBAAgB;AAQrD;;;;;GAKG;AACH,wBAAsB,gCAAgC,CACpD,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC,CA6Bf;AAED;;;;GAIG;AACH,wBAAsB,qCAAqC,CACzD,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED,KAAK,yCAAyC,GAAG;IAC/C,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC;IAClC,+BAA+B,CAAC,EAAE,CAChC,YAAY,EAAE,MAAM,EACpB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KACpC,IAAI,CAAC;CACX,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAsB,mCAAmC,CACvD,MAAM,EAAE,yCAAyC,EACjD,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAwRf"}
1
+ {"version":3,"file":"controller-integration.d.mts","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/controller-integration.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AAOtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,oBAAgB;AAOrD;;;;;GAKG;AACH,wBAAsB,gCAAgC,CACpD,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAiCf;AAED;;;;;;GAMG;AACH,wBAAsB,qCAAqC,CACzD,OAAO,EAAE,qBAAqB,EAC9B,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,IAAI,CAAC,CAwBf;AAED,KAAK,yCAAyC,GAAG;IAC/C,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC;IAClC,+BAA+B,CAAC,EAAE,CAChC,YAAY,EAAE,MAAM,EACpB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KACpC,IAAI,CAAC;CACX,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAsB,mCAAmC,CACvD,MAAM,EAAE,yCAAyC,EACjD,OAAO,EAAE,qBAAqB,EAC9B,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,IAAI,CAAC,CAsRf"}
@@ -1,7 +1,6 @@
1
- import { isEvmAccountType } from "@metamask/keyring-api";
2
1
  import { KeyringTypes } from "@metamask/keyring-controller";
3
2
  import { canPerformAccountSyncing, getInternalAccountsList, getUserStorageAccountsList } from "./sync-utils.mjs";
4
- import { isInternalAccountFromPrimarySRPHdKeyring, isNameDefaultAccountName, mapInternalAccountToUserStorageAccount } from "./utils.mjs";
3
+ import { isNameDefaultAccountName, mapInternalAccountToUserStorageAccount } from "./utils.mjs";
5
4
  import { USER_STORAGE_FEATURE_NAMES } from "../../../shared/storage-schema.mjs";
6
5
  /**
7
6
  * Saves an individual internal account to the user storage.
@@ -12,16 +11,17 @@ import { USER_STORAGE_FEATURE_NAMES } from "../../../shared/storage-schema.mjs";
12
11
  export async function saveInternalAccountToUserStorage(internalAccount, options) {
13
12
  const { getUserStorageControllerInstance } = options;
14
13
  if (!canPerformAccountSyncing(options) ||
15
- !isEvmAccountType(internalAccount.type) ||
16
- !(await isInternalAccountFromPrimarySRPHdKeyring(internalAccount, options))) {
14
+ internalAccount.metadata.keyring.type !== String(KeyringTypes.hd) // sync only EVM accounts until we support multichain accounts
15
+ ) {
17
16
  return;
18
17
  }
18
+ // properties of `options` are (wrongly?) typed as `Json` and eslint crashes if we try to interpret it as such and call a `?.toString()` on it.
19
+ // but we know this is a string?, so we can safely cast it
20
+ const entropySourceId = internalAccount.options.entropySource;
19
21
  try {
20
22
  // Map the internal account to the user storage account schema
21
23
  const mappedAccount = mapInternalAccountToUserStorageAccount(internalAccount);
22
- await getUserStorageControllerInstance().performSetStorage(
23
- // ESLint is confused here.
24
- `${USER_STORAGE_FEATURE_NAMES.accounts}.${internalAccount.address}`, JSON.stringify(mappedAccount));
24
+ await getUserStorageControllerInstance().performSetStorage(`${USER_STORAGE_FEATURE_NAMES.accounts}.${internalAccount.address}`, JSON.stringify(mappedAccount), entropySourceId);
25
25
  }
26
26
  catch (e) {
27
27
  // istanbul ignore next
@@ -33,10 +33,12 @@ export async function saveInternalAccountToUserStorage(internalAccount, options)
33
33
  * Saves the list of internal accounts to the user storage.
34
34
  *
35
35
  * @param options - parameters used for saving the list of internal accounts
36
+ * @param entropySourceId - The entropy source ID used to derive the key,
37
+ * when multiple sources are available (Multi-SRP).
36
38
  */
37
- export async function saveInternalAccountsListToUserStorage(options) {
39
+ export async function saveInternalAccountsListToUserStorage(options, entropySourceId) {
38
40
  const { getUserStorageControllerInstance } = options;
39
- const internalAccountsList = await getInternalAccountsList(options);
41
+ const internalAccountsList = await getInternalAccountsList(options, entropySourceId);
40
42
  if (!internalAccountsList?.length) {
41
43
  return;
42
44
  }
@@ -44,7 +46,7 @@ export async function saveInternalAccountsListToUserStorage(options) {
44
46
  await getUserStorageControllerInstance().performBatchSetStorage(USER_STORAGE_FEATURE_NAMES.accounts, internalAccountsListFormattedForUserStorage.map((account) => [
45
47
  account.a,
46
48
  JSON.stringify(account),
47
- ]));
49
+ ]), entropySourceId);
48
50
  }
49
51
  /**
50
52
  * Syncs the internal accounts list with the user storage accounts list.
@@ -53,8 +55,9 @@ export async function saveInternalAccountsListToUserStorage(options) {
53
55
  *
54
56
  * @param config - parameters used for syncing the internal accounts list with the user storage accounts list
55
57
  * @param options - parameters used for syncing the internal accounts list with the user storage accounts list
58
+ * @param entropySourceId - The entropy source ID used to derive the key,
56
59
  */
57
- export async function syncInternalAccountsWithUserStorage(config, options) {
60
+ export async function syncInternalAccountsWithUserStorage(config, options, entropySourceId) {
58
61
  if (!canPerformAccountSyncing(options)) {
59
62
  return;
60
63
  }
@@ -62,10 +65,9 @@ export async function syncInternalAccountsWithUserStorage(config, options) {
62
65
  const { getMessenger, getUserStorageControllerInstance } = options;
63
66
  try {
64
67
  await getUserStorageControllerInstance().setIsAccountSyncingInProgress(true);
65
- const userStorageAccountsList = await getUserStorageAccountsList(options);
68
+ const userStorageAccountsList = await getUserStorageAccountsList(options, entropySourceId);
66
69
  if (!userStorageAccountsList || !userStorageAccountsList.length) {
67
- await saveInternalAccountsListToUserStorage(options);
68
- await getUserStorageControllerInstance().setHasAccountSyncingSyncedAtLeastOnce(true);
70
+ await saveInternalAccountsListToUserStorage(options, entropySourceId);
69
71
  return;
70
72
  }
71
73
  // Keep a record if erroneous situations are found during the sync
@@ -75,7 +77,7 @@ export async function syncInternalAccountsWithUserStorage(config, options) {
75
77
  const internalAccountsToBeSavedToUserStorage = [];
76
78
  // Compare internal accounts list with user storage accounts list
77
79
  // First step: compare lengths
78
- const internalAccountsList = await getInternalAccountsList(options);
80
+ const internalAccountsList = await getInternalAccountsList(options, entropySourceId);
79
81
  if (!internalAccountsList || !internalAccountsList.length) {
80
82
  throw new Error(`Failed to get internal accounts list`);
81
83
  }
@@ -86,11 +88,8 @@ export async function syncInternalAccountsWithUserStorage(config, options) {
86
88
  const numberOfAccountsToAdd = Math.min(userStorageAccountsList.length, maxNumberOfAccountsToAdd) -
87
89
  internalAccountsList.length;
88
90
  // Create new accounts to match the user storage accounts list
89
- // NOTE: we only support the primary SRP HD keyring for now
90
- // This is why we are hardcoding the index to 0
91
91
  await getMessenger().call('KeyringController:withKeyring', {
92
- type: KeyringTypes.hd,
93
- index: 0,
92
+ id: entropySourceId,
94
93
  }, async ({ keyring }) => {
95
94
  await keyring.addAccounts(numberOfAccountsToAdd);
96
95
  });
@@ -101,7 +100,7 @@ export async function syncInternalAccountsWithUserStorage(config, options) {
101
100
  }
102
101
  // Second step: compare account names
103
102
  // Get the internal accounts list again since new accounts might have been added in the previous step
104
- const refreshedInternalAccountsList = await getInternalAccountsList(options);
103
+ const refreshedInternalAccountsList = await getInternalAccountsList(options, entropySourceId);
105
104
  const newlyAddedAccounts = refreshedInternalAccountsList.filter((account) => !internalAccountsList.find((a) => a.address === account.address));
106
105
  for (const internalAccount of refreshedInternalAccountsList) {
107
106
  const userStorageAccount = userStorageAccountsList.find((account) => account.a === internalAccount.address);
@@ -177,13 +176,13 @@ export async function syncInternalAccountsWithUserStorage(config, options) {
177
176
  await getUserStorageControllerInstance().performBatchSetStorage(USER_STORAGE_FEATURE_NAMES.accounts, internalAccountsToBeSavedToUserStorage.map((account) => [
178
177
  account.address,
179
178
  JSON.stringify(mapInternalAccountToUserStorageAccount(account)),
180
- ]));
179
+ ]), entropySourceId);
181
180
  }
182
181
  // In case we have corrupted user storage with accounts that don't exist in the internal accounts list
183
182
  // Delete those accounts from the user storage
184
183
  const userStorageAccountsToBeDeleted = userStorageAccountsList.filter((account) => !refreshedInternalAccountsList.find((a) => a.address === account.a));
185
184
  if (userStorageAccountsToBeDeleted.length) {
186
- await getUserStorageControllerInstance().performBatchDeleteStorage(USER_STORAGE_FEATURE_NAMES.accounts, userStorageAccountsToBeDeleted.map((account) => account.a));
185
+ await getUserStorageControllerInstance().performBatchDeleteStorage(USER_STORAGE_FEATURE_NAMES.accounts, userStorageAccountsToBeDeleted.map((account) => account.a), entropySourceId);
187
186
  erroneousSituationsFound = true;
188
187
  onAccountSyncErroneousSituation?.('An account was present in the user storage accounts list but was not found in the internal accounts list after the sync', {
189
188
  userStorageAccountsToBeDeleted,
@@ -195,8 +194,8 @@ export async function syncInternalAccountsWithUserStorage(config, options) {
195
194
  }
196
195
  if (erroneousSituationsFound) {
197
196
  const [finalUserStorageAccountsList, finalInternalAccountsList] = await Promise.all([
198
- getUserStorageAccountsList(options),
199
- getInternalAccountsList(options),
197
+ getUserStorageAccountsList(options, entropySourceId),
198
+ getInternalAccountsList(options, entropySourceId),
200
199
  ]);
201
200
  const doesEveryAccountInInternalAccountsListExistInUserStorageAccountsList = finalInternalAccountsList.every((account) => finalUserStorageAccountsList?.some((userStorageAccount) => userStorageAccount.a === account.address));
202
201
  // istanbul ignore next
@@ -216,9 +215,6 @@ export async function syncInternalAccountsWithUserStorage(config, options) {
216
215
  onAccountSyncErroneousSituation?.('Erroneous situations were found during the sync, and final state does not match the expected state', context);
217
216
  }
218
217
  }
219
- // We do this here and not in the finally statement because we want to make sure that
220
- // the accounts are saved / updated / deleted at least once before we set this flag
221
- await getUserStorageControllerInstance().setHasAccountSyncingSyncedAtLeastOnce(true);
222
218
  }
223
219
  catch (e) {
224
220
  // istanbul ignore next
@@ -1 +1 @@
1
- {"version":3,"file":"controller-integration.mjs","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/controller-integration.ts"],"names":[],"mappings":"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","sourcesContent":["import { isEvmAccountType } from '@metamask/keyring-api';\nimport { KeyringTypes } from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\n\nimport {\n canPerformAccountSyncing,\n getInternalAccountsList,\n getUserStorageAccountsList,\n} from './sync-utils';\nimport type { AccountSyncingOptions } from './types';\nimport {\n isInternalAccountFromPrimarySRPHdKeyring,\n isNameDefaultAccountName,\n mapInternalAccountToUserStorageAccount,\n} from './utils';\nimport { USER_STORAGE_FEATURE_NAMES } from '../../../shared/storage-schema';\n\n/**\n * Saves an individual internal account to the user storage.\n *\n * @param internalAccount - The internal account to save\n * @param options - parameters used for saving the internal account\n */\nexport async function saveInternalAccountToUserStorage(\n internalAccount: InternalAccount,\n options: AccountSyncingOptions,\n): Promise<void> {\n const { getUserStorageControllerInstance } = options;\n\n if (\n !canPerformAccountSyncing(options) ||\n !isEvmAccountType(internalAccount.type) ||\n !(await isInternalAccountFromPrimarySRPHdKeyring(internalAccount, options))\n ) {\n return;\n }\n\n try {\n // Map the internal account to the user storage account schema\n const mappedAccount =\n mapInternalAccountToUserStorageAccount(internalAccount);\n\n await getUserStorageControllerInstance().performSetStorage(\n // ESLint is confused here.\n\n `${USER_STORAGE_FEATURE_NAMES.accounts}.${internalAccount.address}`,\n JSON.stringify(mappedAccount),\n );\n } catch (e) {\n // istanbul ignore next\n const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);\n throw new Error(\n `UserStorageController - failed to save account to user storage - ${errorMessage}`,\n );\n }\n}\n\n/**\n * Saves the list of internal accounts to the user storage.\n *\n * @param options - parameters used for saving the list of internal accounts\n */\nexport async function saveInternalAccountsListToUserStorage(\n options: AccountSyncingOptions,\n): Promise<void> {\n const { getUserStorageControllerInstance } = options;\n\n const internalAccountsList = await getInternalAccountsList(options);\n\n if (!internalAccountsList?.length) {\n return;\n }\n\n const internalAccountsListFormattedForUserStorage = internalAccountsList.map(\n mapInternalAccountToUserStorageAccount,\n );\n\n await getUserStorageControllerInstance().performBatchSetStorage(\n USER_STORAGE_FEATURE_NAMES.accounts,\n internalAccountsListFormattedForUserStorage.map((account) => [\n account.a,\n JSON.stringify(account),\n ]),\n );\n}\n\ntype SyncInternalAccountsWithUserStorageConfig = {\n maxNumberOfAccountsToAdd?: number;\n onAccountAdded?: () => void;\n onAccountNameUpdated?: () => void;\n onAccountSyncErroneousSituation?: (\n errorMessage: string,\n sentryContext?: Record<string, unknown>,\n ) => void;\n};\n\n/**\n * Syncs the internal accounts list with the user storage accounts list.\n * This method is used to make sure that the internal accounts list is up-to-date with the user storage accounts list and vice-versa.\n * It will add new accounts to the internal accounts list, update/merge conflicting names and re-upload the results in some cases to the user storage.\n *\n * @param config - parameters used for syncing the internal accounts list with the user storage accounts list\n * @param options - parameters used for syncing the internal accounts list with the user storage accounts list\n */\nexport async function syncInternalAccountsWithUserStorage(\n config: SyncInternalAccountsWithUserStorageConfig,\n options: AccountSyncingOptions,\n): Promise<void> {\n if (!canPerformAccountSyncing(options)) {\n return;\n }\n\n const {\n maxNumberOfAccountsToAdd = Infinity,\n onAccountAdded,\n onAccountNameUpdated,\n onAccountSyncErroneousSituation,\n } = config;\n const { getMessenger, getUserStorageControllerInstance } = options;\n\n try {\n await getUserStorageControllerInstance().setIsAccountSyncingInProgress(\n true,\n );\n\n const userStorageAccountsList = await getUserStorageAccountsList(options);\n\n if (!userStorageAccountsList || !userStorageAccountsList.length) {\n await saveInternalAccountsListToUserStorage(options);\n await getUserStorageControllerInstance().setHasAccountSyncingSyncedAtLeastOnce(\n true,\n );\n return;\n }\n // Keep a record if erroneous situations are found during the sync\n // This is done so we can send the context to Sentry in case of an erroneous situation\n let erroneousSituationsFound = false;\n\n // Prepare an array of internal accounts to be saved to the user storage\n const internalAccountsToBeSavedToUserStorage: InternalAccount[] = [];\n\n // Compare internal accounts list with user storage accounts list\n // First step: compare lengths\n const internalAccountsList = await getInternalAccountsList(options);\n\n if (!internalAccountsList || !internalAccountsList.length) {\n throw new Error(`Failed to get internal accounts list`);\n }\n\n const hasMoreUserStorageAccountsThanInternalAccounts =\n userStorageAccountsList.length > internalAccountsList.length;\n\n // We don't want to remove existing accounts for a user\n // so we only add new accounts if the user has more accounts in user storage than internal accounts\n if (hasMoreUserStorageAccountsThanInternalAccounts) {\n const numberOfAccountsToAdd =\n Math.min(userStorageAccountsList.length, maxNumberOfAccountsToAdd) -\n internalAccountsList.length;\n\n // Create new accounts to match the user storage accounts list\n // NOTE: we only support the primary SRP HD keyring for now\n // This is why we are hardcoding the index to 0\n await getMessenger().call(\n 'KeyringController:withKeyring',\n {\n type: KeyringTypes.hd,\n index: 0,\n },\n async ({ keyring }) => {\n await keyring.addAccounts(numberOfAccountsToAdd);\n },\n );\n\n // TODO: below code is kept for analytics but should probably be re-thought\n for (let i = 0; i < numberOfAccountsToAdd; i++) {\n onAccountAdded?.();\n }\n }\n\n // Second step: compare account names\n // Get the internal accounts list again since new accounts might have been added in the previous step\n const refreshedInternalAccountsList =\n await getInternalAccountsList(options);\n\n const newlyAddedAccounts = refreshedInternalAccountsList.filter(\n (account) =>\n !internalAccountsList.find((a) => a.address === account.address),\n );\n\n for (const internalAccount of refreshedInternalAccountsList) {\n const userStorageAccount = userStorageAccountsList.find(\n (account) => account.a === internalAccount.address,\n );\n\n // If the account is not present in user storage\n // istanbul ignore next\n if (!userStorageAccount) {\n // If the account was just added in the previous step, skip saving it, it's likely to be a bogus account\n if (newlyAddedAccounts.includes(internalAccount)) {\n erroneousSituationsFound = true;\n onAccountSyncErroneousSituation?.(\n 'An account was added to the internal accounts list but was not present in the user storage accounts list',\n {\n internalAccount,\n userStorageAccount,\n newlyAddedAccounts,\n userStorageAccountsList,\n internalAccountsList,\n refreshedInternalAccountsList,\n internalAccountsToBeSavedToUserStorage,\n },\n );\n continue;\n }\n // Otherwise, it means that this internal account was present before the sync, and needs to be saved to the user storage\n internalAccountsToBeSavedToUserStorage.push(internalAccount);\n continue;\n }\n\n // From this point on, we know that the account is present in\n // both the internal accounts list and the user storage accounts list\n\n // One or both accounts have default names\n const isInternalAccountNameDefault = isNameDefaultAccountName(\n internalAccount.metadata.name,\n );\n const isUserStorageAccountNameDefault = isNameDefaultAccountName(\n userStorageAccount.n,\n );\n\n // Internal account has default name\n if (isInternalAccountNameDefault) {\n if (!isUserStorageAccountNameDefault) {\n getMessenger().call(\n 'AccountsController:updateAccountMetadata',\n internalAccount.id,\n {\n name: userStorageAccount.n,\n },\n );\n\n onAccountNameUpdated?.();\n }\n continue;\n }\n\n // Internal account has custom name but user storage account has default name\n if (isUserStorageAccountNameDefault) {\n internalAccountsToBeSavedToUserStorage.push(internalAccount);\n continue;\n }\n\n // Both accounts have custom names\n\n // User storage account has a nameLastUpdatedAt timestamp\n // Note: not storing the undefined checks in constants to act as a type guard\n if (userStorageAccount.nlu !== undefined) {\n if (internalAccount.metadata.nameLastUpdatedAt !== undefined) {\n const isInternalAccountNameNewer =\n internalAccount.metadata.nameLastUpdatedAt > userStorageAccount.nlu;\n\n if (isInternalAccountNameNewer) {\n internalAccountsToBeSavedToUserStorage.push(internalAccount);\n continue;\n }\n }\n\n getMessenger().call(\n 'AccountsController:updateAccountMetadata',\n internalAccount.id,\n {\n name: userStorageAccount.n,\n nameLastUpdatedAt: userStorageAccount.nlu,\n },\n );\n\n const areInternalAndUserStorageAccountNamesEqual =\n internalAccount.metadata.name === userStorageAccount.n;\n\n if (!areInternalAndUserStorageAccountNamesEqual) {\n onAccountNameUpdated?.();\n }\n\n continue;\n } else if (internalAccount.metadata.nameLastUpdatedAt !== undefined) {\n internalAccountsToBeSavedToUserStorage.push(internalAccount);\n continue;\n }\n }\n\n // Save the internal accounts list to the user storage\n if (internalAccountsToBeSavedToUserStorage.length) {\n await getUserStorageControllerInstance().performBatchSetStorage(\n USER_STORAGE_FEATURE_NAMES.accounts,\n internalAccountsToBeSavedToUserStorage.map((account) => [\n account.address,\n JSON.stringify(mapInternalAccountToUserStorageAccount(account)),\n ]),\n );\n }\n\n // In case we have corrupted user storage with accounts that don't exist in the internal accounts list\n // Delete those accounts from the user storage\n const userStorageAccountsToBeDeleted = userStorageAccountsList.filter(\n (account) =>\n !refreshedInternalAccountsList.find((a) => a.address === account.a),\n );\n\n if (userStorageAccountsToBeDeleted.length) {\n await getUserStorageControllerInstance().performBatchDeleteStorage(\n USER_STORAGE_FEATURE_NAMES.accounts,\n userStorageAccountsToBeDeleted.map((account) => account.a),\n );\n erroneousSituationsFound = true;\n onAccountSyncErroneousSituation?.(\n 'An account was present in the user storage accounts list but was not found in the internal accounts list after the sync',\n {\n userStorageAccountsToBeDeleted,\n internalAccountsList,\n refreshedInternalAccountsList,\n internalAccountsToBeSavedToUserStorage,\n userStorageAccountsList,\n },\n );\n }\n\n if (erroneousSituationsFound) {\n const [finalUserStorageAccountsList, finalInternalAccountsList] =\n await Promise.all([\n getUserStorageAccountsList(options),\n getInternalAccountsList(options),\n ]);\n\n const doesEveryAccountInInternalAccountsListExistInUserStorageAccountsList =\n finalInternalAccountsList.every((account) =>\n finalUserStorageAccountsList?.some(\n (userStorageAccount) => userStorageAccount.a === account.address,\n ),\n );\n\n // istanbul ignore next\n const doesEveryAccountInUserStorageAccountsListExistInInternalAccountsList =\n (finalUserStorageAccountsList?.length || 0) > maxNumberOfAccountsToAdd\n ? true\n : finalUserStorageAccountsList?.every((account) =>\n finalInternalAccountsList.some(\n (internalAccount) => internalAccount.address === account.a,\n ),\n );\n\n const doFinalListsMatch =\n doesEveryAccountInInternalAccountsListExistInUserStorageAccountsList &&\n doesEveryAccountInUserStorageAccountsListExistInInternalAccountsList;\n\n const context = {\n finalUserStorageAccountsList,\n finalInternalAccountsList,\n };\n if (doFinalListsMatch) {\n onAccountSyncErroneousSituation?.(\n 'Erroneous situations were found during the sync, but final state matches the expected state',\n context,\n );\n } else {\n onAccountSyncErroneousSituation?.(\n 'Erroneous situations were found during the sync, and final state does not match the expected state',\n context,\n );\n }\n }\n\n // We do this here and not in the finally statement because we want to make sure that\n // the accounts are saved / updated / deleted at least once before we set this flag\n await getUserStorageControllerInstance().setHasAccountSyncingSyncedAtLeastOnce(\n true,\n );\n } catch (e) {\n // istanbul ignore next\n const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);\n throw new Error(\n `UserStorageController - failed to sync user storage accounts list - ${errorMessage}`,\n );\n } finally {\n await getUserStorageControllerInstance().setIsAccountSyncingInProgress(\n false,\n );\n }\n}\n"]}
1
+ {"version":3,"file":"controller-integration.mjs","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/controller-integration.ts"],"names":[],"mappings":"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","sourcesContent":["import { KeyringTypes } from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\n\nimport {\n canPerformAccountSyncing,\n getInternalAccountsList,\n getUserStorageAccountsList,\n} from './sync-utils';\nimport type { AccountSyncingOptions } from './types';\nimport {\n isNameDefaultAccountName,\n mapInternalAccountToUserStorageAccount,\n} from './utils';\nimport { USER_STORAGE_FEATURE_NAMES } from '../../../shared/storage-schema';\n\n/**\n * Saves an individual internal account to the user storage.\n *\n * @param internalAccount - The internal account to save\n * @param options - parameters used for saving the internal account\n */\nexport async function saveInternalAccountToUserStorage(\n internalAccount: InternalAccount,\n options: AccountSyncingOptions,\n): Promise<void> {\n const { getUserStorageControllerInstance } = options;\n\n if (\n !canPerformAccountSyncing(options) ||\n internalAccount.metadata.keyring.type !== String(KeyringTypes.hd) // sync only EVM accounts until we support multichain accounts\n ) {\n return;\n }\n\n // properties of `options` are (wrongly?) typed as `Json` and eslint crashes if we try to interpret it as such and call a `?.toString()` on it.\n // but we know this is a string?, so we can safely cast it\n const entropySourceId = internalAccount.options.entropySource as\n | string\n | undefined;\n\n try {\n // Map the internal account to the user storage account schema\n const mappedAccount =\n mapInternalAccountToUserStorageAccount(internalAccount);\n\n await getUserStorageControllerInstance().performSetStorage(\n `${USER_STORAGE_FEATURE_NAMES.accounts}.${internalAccount.address}`,\n JSON.stringify(mappedAccount),\n entropySourceId,\n );\n } catch (e) {\n // istanbul ignore next\n const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);\n throw new Error(\n `UserStorageController - failed to save account to user storage - ${errorMessage}`,\n );\n }\n}\n\n/**\n * Saves the list of internal accounts to the user storage.\n *\n * @param options - parameters used for saving the list of internal accounts\n * @param entropySourceId - The entropy source ID used to derive the key,\n * when multiple sources are available (Multi-SRP).\n */\nexport async function saveInternalAccountsListToUserStorage(\n options: AccountSyncingOptions,\n entropySourceId: string,\n): Promise<void> {\n const { getUserStorageControllerInstance } = options;\n\n const internalAccountsList = await getInternalAccountsList(\n options,\n entropySourceId,\n );\n\n if (!internalAccountsList?.length) {\n return;\n }\n\n const internalAccountsListFormattedForUserStorage = internalAccountsList.map(\n mapInternalAccountToUserStorageAccount,\n );\n\n await getUserStorageControllerInstance().performBatchSetStorage(\n USER_STORAGE_FEATURE_NAMES.accounts,\n internalAccountsListFormattedForUserStorage.map((account) => [\n account.a,\n JSON.stringify(account),\n ]),\n entropySourceId,\n );\n}\n\ntype SyncInternalAccountsWithUserStorageConfig = {\n maxNumberOfAccountsToAdd?: number;\n onAccountAdded?: () => void;\n onAccountNameUpdated?: () => void;\n onAccountSyncErroneousSituation?: (\n errorMessage: string,\n sentryContext?: Record<string, unknown>,\n ) => void;\n};\n\n/**\n * Syncs the internal accounts list with the user storage accounts list.\n * This method is used to make sure that the internal accounts list is up-to-date with the user storage accounts list and vice-versa.\n * It will add new accounts to the internal accounts list, update/merge conflicting names and re-upload the results in some cases to the user storage.\n *\n * @param config - parameters used for syncing the internal accounts list with the user storage accounts list\n * @param options - parameters used for syncing the internal accounts list with the user storage accounts list\n * @param entropySourceId - The entropy source ID used to derive the key,\n */\nexport async function syncInternalAccountsWithUserStorage(\n config: SyncInternalAccountsWithUserStorageConfig,\n options: AccountSyncingOptions,\n entropySourceId: string,\n): Promise<void> {\n if (!canPerformAccountSyncing(options)) {\n return;\n }\n\n const {\n maxNumberOfAccountsToAdd = Infinity,\n onAccountAdded,\n onAccountNameUpdated,\n onAccountSyncErroneousSituation,\n } = config;\n const { getMessenger, getUserStorageControllerInstance } = options;\n\n try {\n await getUserStorageControllerInstance().setIsAccountSyncingInProgress(\n true,\n );\n\n const userStorageAccountsList = await getUserStorageAccountsList(\n options,\n entropySourceId,\n );\n\n if (!userStorageAccountsList || !userStorageAccountsList.length) {\n await saveInternalAccountsListToUserStorage(options, entropySourceId);\n return;\n }\n // Keep a record if erroneous situations are found during the sync\n // This is done so we can send the context to Sentry in case of an erroneous situation\n let erroneousSituationsFound = false;\n\n // Prepare an array of internal accounts to be saved to the user storage\n const internalAccountsToBeSavedToUserStorage: InternalAccount[] = [];\n\n // Compare internal accounts list with user storage accounts list\n // First step: compare lengths\n const internalAccountsList = await getInternalAccountsList(\n options,\n entropySourceId,\n );\n\n if (!internalAccountsList || !internalAccountsList.length) {\n throw new Error(`Failed to get internal accounts list`);\n }\n\n const hasMoreUserStorageAccountsThanInternalAccounts =\n userStorageAccountsList.length > internalAccountsList.length;\n\n // We don't want to remove existing accounts for a user\n // so we only add new accounts if the user has more accounts in user storage than internal accounts\n if (hasMoreUserStorageAccountsThanInternalAccounts) {\n const numberOfAccountsToAdd =\n Math.min(userStorageAccountsList.length, maxNumberOfAccountsToAdd) -\n internalAccountsList.length;\n\n // Create new accounts to match the user storage accounts list\n await getMessenger().call(\n 'KeyringController:withKeyring',\n {\n id: entropySourceId,\n },\n async ({ keyring }) => {\n await keyring.addAccounts(numberOfAccountsToAdd);\n },\n );\n\n // TODO: below code is kept for analytics but should probably be re-thought\n for (let i = 0; i < numberOfAccountsToAdd; i++) {\n onAccountAdded?.();\n }\n }\n\n // Second step: compare account names\n // Get the internal accounts list again since new accounts might have been added in the previous step\n const refreshedInternalAccountsList = await getInternalAccountsList(\n options,\n entropySourceId,\n );\n\n const newlyAddedAccounts = refreshedInternalAccountsList.filter(\n (account) =>\n !internalAccountsList.find((a) => a.address === account.address),\n );\n\n for (const internalAccount of refreshedInternalAccountsList) {\n const userStorageAccount = userStorageAccountsList.find(\n (account) => account.a === internalAccount.address,\n );\n\n // If the account is not present in user storage\n // istanbul ignore next\n if (!userStorageAccount) {\n // If the account was just added in the previous step, skip saving it, it's likely to be a bogus account\n if (newlyAddedAccounts.includes(internalAccount)) {\n erroneousSituationsFound = true;\n onAccountSyncErroneousSituation?.(\n 'An account was added to the internal accounts list but was not present in the user storage accounts list',\n {\n internalAccount,\n userStorageAccount,\n newlyAddedAccounts,\n userStorageAccountsList,\n internalAccountsList,\n refreshedInternalAccountsList,\n internalAccountsToBeSavedToUserStorage,\n },\n );\n continue;\n }\n // Otherwise, it means that this internal account was present before the sync, and needs to be saved to the user storage\n internalAccountsToBeSavedToUserStorage.push(internalAccount);\n continue;\n }\n\n // From this point on, we know that the account is present in\n // both the internal accounts list and the user storage accounts list\n\n // One or both accounts have default names\n const isInternalAccountNameDefault = isNameDefaultAccountName(\n internalAccount.metadata.name,\n );\n const isUserStorageAccountNameDefault = isNameDefaultAccountName(\n userStorageAccount.n,\n );\n\n // Internal account has default name\n if (isInternalAccountNameDefault) {\n if (!isUserStorageAccountNameDefault) {\n getMessenger().call(\n 'AccountsController:updateAccountMetadata',\n internalAccount.id,\n {\n name: userStorageAccount.n,\n },\n );\n\n onAccountNameUpdated?.();\n }\n continue;\n }\n\n // Internal account has custom name but user storage account has default name\n if (isUserStorageAccountNameDefault) {\n internalAccountsToBeSavedToUserStorage.push(internalAccount);\n continue;\n }\n\n // Both accounts have custom names\n\n // User storage account has a nameLastUpdatedAt timestamp\n // Note: not storing the undefined checks in constants to act as a type guard\n if (userStorageAccount.nlu !== undefined) {\n if (internalAccount.metadata.nameLastUpdatedAt !== undefined) {\n const isInternalAccountNameNewer =\n internalAccount.metadata.nameLastUpdatedAt > userStorageAccount.nlu;\n\n if (isInternalAccountNameNewer) {\n internalAccountsToBeSavedToUserStorage.push(internalAccount);\n continue;\n }\n }\n\n getMessenger().call(\n 'AccountsController:updateAccountMetadata',\n internalAccount.id,\n {\n name: userStorageAccount.n,\n nameLastUpdatedAt: userStorageAccount.nlu,\n },\n );\n\n const areInternalAndUserStorageAccountNamesEqual =\n internalAccount.metadata.name === userStorageAccount.n;\n\n if (!areInternalAndUserStorageAccountNamesEqual) {\n onAccountNameUpdated?.();\n }\n\n continue;\n } else if (internalAccount.metadata.nameLastUpdatedAt !== undefined) {\n internalAccountsToBeSavedToUserStorage.push(internalAccount);\n continue;\n }\n }\n\n // Save the internal accounts list to the user storage\n if (internalAccountsToBeSavedToUserStorage.length) {\n await getUserStorageControllerInstance().performBatchSetStorage(\n USER_STORAGE_FEATURE_NAMES.accounts,\n internalAccountsToBeSavedToUserStorage.map((account) => [\n account.address,\n JSON.stringify(mapInternalAccountToUserStorageAccount(account)),\n ]),\n entropySourceId,\n );\n }\n\n // In case we have corrupted user storage with accounts that don't exist in the internal accounts list\n // Delete those accounts from the user storage\n const userStorageAccountsToBeDeleted = userStorageAccountsList.filter(\n (account) =>\n !refreshedInternalAccountsList.find((a) => a.address === account.a),\n );\n\n if (userStorageAccountsToBeDeleted.length) {\n await getUserStorageControllerInstance().performBatchDeleteStorage(\n USER_STORAGE_FEATURE_NAMES.accounts,\n userStorageAccountsToBeDeleted.map((account) => account.a),\n entropySourceId,\n );\n erroneousSituationsFound = true;\n onAccountSyncErroneousSituation?.(\n 'An account was present in the user storage accounts list but was not found in the internal accounts list after the sync',\n {\n userStorageAccountsToBeDeleted,\n internalAccountsList,\n refreshedInternalAccountsList,\n internalAccountsToBeSavedToUserStorage,\n userStorageAccountsList,\n },\n );\n }\n\n if (erroneousSituationsFound) {\n const [finalUserStorageAccountsList, finalInternalAccountsList] =\n await Promise.all([\n getUserStorageAccountsList(options, entropySourceId),\n getInternalAccountsList(options, entropySourceId),\n ]);\n\n const doesEveryAccountInInternalAccountsListExistInUserStorageAccountsList =\n finalInternalAccountsList.every((account) =>\n finalUserStorageAccountsList?.some(\n (userStorageAccount) => userStorageAccount.a === account.address,\n ),\n );\n\n // istanbul ignore next\n const doesEveryAccountInUserStorageAccountsListExistInInternalAccountsList =\n (finalUserStorageAccountsList?.length || 0) > maxNumberOfAccountsToAdd\n ? true\n : finalUserStorageAccountsList?.every((account) =>\n finalInternalAccountsList.some(\n (internalAccount) => internalAccount.address === account.a,\n ),\n );\n\n const doFinalListsMatch =\n doesEveryAccountInInternalAccountsListExistInUserStorageAccountsList &&\n doesEveryAccountInUserStorageAccountsListExistInInternalAccountsList;\n\n const context = {\n finalUserStorageAccountsList,\n finalInternalAccountsList,\n };\n if (doFinalListsMatch) {\n onAccountSyncErroneousSituation?.(\n 'Erroneous situations were found during the sync, but final state matches the expected state',\n context,\n );\n } else {\n onAccountSyncErroneousSituation?.(\n 'Erroneous situations were found during the sync, and final state does not match the expected state',\n context,\n );\n }\n }\n } catch (e) {\n // istanbul ignore next\n const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);\n throw new Error(\n `UserStorageController - failed to sync user storage accounts list - ${errorMessage}`,\n );\n } finally {\n await getUserStorageControllerInstance().setIsAccountSyncingInProgress(\n false,\n );\n }\n}\n"]}
@@ -10,19 +10,9 @@ const sync_utils_1 = require("./sync-utils.cjs");
10
10
  */
11
11
  function setupAccountSyncingSubscriptions(options) {
12
12
  const { getMessenger, getUserStorageControllerInstance } = options;
13
- getMessenger().subscribe('AccountsController:accountAdded',
14
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
15
- async (account) => {
16
- if (!(0, sync_utils_1.canPerformAccountSyncing)(options) ||
17
- !getUserStorageControllerInstance().state
18
- .hasAccountSyncingSyncedAtLeastOnce) {
19
- return;
20
- }
21
- await (0, controller_integration_1.saveInternalAccountToUserStorage)(account, options);
22
- });
23
- getMessenger().subscribe('AccountsController:accountRenamed',
24
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
25
- async (account) => {
13
+ // We don't listen to `AccountsController:accountAdded`
14
+ // because it publishes `AccountsController:accountRenamed` in any case.
15
+ getMessenger().subscribe('AccountsController:accountRenamed', async (account) => {
26
16
  if (!(0, sync_utils_1.canPerformAccountSyncing)(options) ||
27
17
  !getUserStorageControllerInstance().state
28
18
  .hasAccountSyncingSyncedAtLeastOnce) {
@@ -1 +1 @@
1
- {"version":3,"file":"setup-subscriptions.cjs","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/setup-subscriptions.ts"],"names":[],"mappings":";;;AAAA,yEAA4E;AAC5E,iDAAwD;AAGxD;;;;GAIG;AACH,SAAgB,gCAAgC,CAC9C,OAA8B;IAE9B,MAAM,EAAE,YAAY,EAAE,gCAAgC,EAAE,GAAG,OAAO,CAAC;IAEnE,YAAY,EAAE,CAAC,SAAS,CACtB,iCAAiC;IAEjC,kEAAkE;IAClE,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,IACE,CAAC,IAAA,qCAAwB,EAAC,OAAO,CAAC;YAClC,CAAC,gCAAgC,EAAE,CAAC,KAAK;iBACtC,kCAAkC,EACrC;YACA,OAAO;SACR;QAED,MAAM,IAAA,yDAAgC,EAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC3D,CAAC,CACF,CAAC;IAEF,YAAY,EAAE,CAAC,SAAS,CACtB,mCAAmC;IAEnC,kEAAkE;IAClE,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,IACE,CAAC,IAAA,qCAAwB,EAAC,OAAO,CAAC;YAClC,CAAC,gCAAgC,EAAE,CAAC,KAAK;iBACtC,kCAAkC,EACrC;YACA,OAAO;SACR;QAED,MAAM,IAAA,yDAAgC,EAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC3D,CAAC,CACF,CAAC;AACJ,CAAC;AAtCD,4EAsCC","sourcesContent":["import { saveInternalAccountToUserStorage } from './controller-integration';\nimport { canPerformAccountSyncing } from './sync-utils';\nimport type { AccountSyncingOptions } from './types';\n\n/**\n * Initialize and setup events to listen to for account syncing\n *\n * @param options - parameters used for initializing and enabling account syncing\n */\nexport function setupAccountSyncingSubscriptions(\n options: AccountSyncingOptions,\n) {\n const { getMessenger, getUserStorageControllerInstance } = options;\n\n getMessenger().subscribe(\n 'AccountsController:accountAdded',\n\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n async (account) => {\n if (\n !canPerformAccountSyncing(options) ||\n !getUserStorageControllerInstance().state\n .hasAccountSyncingSyncedAtLeastOnce\n ) {\n return;\n }\n\n await saveInternalAccountToUserStorage(account, options);\n },\n );\n\n getMessenger().subscribe(\n 'AccountsController:accountRenamed',\n\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n async (account) => {\n if (\n !canPerformAccountSyncing(options) ||\n !getUserStorageControllerInstance().state\n .hasAccountSyncingSyncedAtLeastOnce\n ) {\n return;\n }\n\n await saveInternalAccountToUserStorage(account, options);\n },\n );\n}\n"]}
1
+ {"version":3,"file":"setup-subscriptions.cjs","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/setup-subscriptions.ts"],"names":[],"mappings":";;;AAAA,yEAA4E;AAC5E,iDAAwD;AAGxD;;;;GAIG;AACH,SAAgB,gCAAgC,CAC9C,OAA8B;IAE9B,MAAM,EAAE,YAAY,EAAE,gCAAgC,EAAE,GAAG,OAAO,CAAC;IAEnE,uDAAuD;IACvD,wEAAwE;IACxE,YAAY,EAAE,CAAC,SAAS,CACtB,mCAAmC,EAEnC,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,IACE,CAAC,IAAA,qCAAwB,EAAC,OAAO,CAAC;YAClC,CAAC,gCAAgC,EAAE,CAAC,KAAK;iBACtC,kCAAkC,EACrC;YACA,OAAO;SACR;QAED,MAAM,IAAA,yDAAgC,EAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC3D,CAAC,CACF,CAAC;AACJ,CAAC;AAtBD,4EAsBC","sourcesContent":["import { saveInternalAccountToUserStorage } from './controller-integration';\nimport { canPerformAccountSyncing } from './sync-utils';\nimport type { AccountSyncingOptions } from './types';\n\n/**\n * Initialize and setup events to listen to for account syncing\n *\n * @param options - parameters used for initializing and enabling account syncing\n */\nexport function setupAccountSyncingSubscriptions(\n options: AccountSyncingOptions,\n) {\n const { getMessenger, getUserStorageControllerInstance } = options;\n\n // We don't listen to `AccountsController:accountAdded`\n // because it publishes `AccountsController:accountRenamed` in any case.\n getMessenger().subscribe(\n 'AccountsController:accountRenamed',\n\n async (account) => {\n if (\n !canPerformAccountSyncing(options) ||\n !getUserStorageControllerInstance().state\n .hasAccountSyncingSyncedAtLeastOnce\n ) {\n return;\n }\n\n await saveInternalAccountToUserStorage(account, options);\n },\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"setup-subscriptions.d.cts","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/setup-subscriptions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,oBAAgB;AAErD;;;;GAIG;AACH,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,qBAAqB,QAqC/B"}
1
+ {"version":3,"file":"setup-subscriptions.d.cts","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/setup-subscriptions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,oBAAgB;AAErD;;;;GAIG;AACH,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,qBAAqB,QAqB/B"}
@@ -1 +1 @@
1
- {"version":3,"file":"setup-subscriptions.d.mts","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/setup-subscriptions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,oBAAgB;AAErD;;;;GAIG;AACH,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,qBAAqB,QAqC/B"}
1
+ {"version":3,"file":"setup-subscriptions.d.mts","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/setup-subscriptions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,oBAAgB;AAErD;;;;GAIG;AACH,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,qBAAqB,QAqB/B"}