@metamask-previews/profile-sync-controller 19.0.0-preview-29bd474 → 19.0.0-preview-6fc9506d

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 (38) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/controllers/user-storage/UserStorageController.cjs +10 -3
  3. package/dist/controllers/user-storage/UserStorageController.cjs.map +1 -1
  4. package/dist/controllers/user-storage/UserStorageController.d.cts +3 -1
  5. package/dist/controllers/user-storage/UserStorageController.d.cts.map +1 -1
  6. package/dist/controllers/user-storage/UserStorageController.d.mts +3 -1
  7. package/dist/controllers/user-storage/UserStorageController.d.mts.map +1 -1
  8. package/dist/controllers/user-storage/UserStorageController.mjs +10 -3
  9. package/dist/controllers/user-storage/UserStorageController.mjs.map +1 -1
  10. package/dist/controllers/user-storage/account-syncing/controller-integration.cjs +184 -168
  11. package/dist/controllers/user-storage/account-syncing/controller-integration.cjs.map +1 -1
  12. package/dist/controllers/user-storage/account-syncing/controller-integration.d.cts +3 -0
  13. package/dist/controllers/user-storage/account-syncing/controller-integration.d.cts.map +1 -1
  14. package/dist/controllers/user-storage/account-syncing/controller-integration.d.mts +3 -0
  15. package/dist/controllers/user-storage/account-syncing/controller-integration.d.mts.map +1 -1
  16. package/dist/controllers/user-storage/account-syncing/controller-integration.mjs +184 -168
  17. package/dist/controllers/user-storage/account-syncing/controller-integration.mjs.map +1 -1
  18. package/dist/controllers/user-storage/account-syncing/types.cjs.map +1 -1
  19. package/dist/controllers/user-storage/account-syncing/types.d.cts +2 -0
  20. package/dist/controllers/user-storage/account-syncing/types.d.cts.map +1 -1
  21. package/dist/controllers/user-storage/account-syncing/types.d.mts +2 -0
  22. package/dist/controllers/user-storage/account-syncing/types.d.mts.map +1 -1
  23. package/dist/controllers/user-storage/account-syncing/types.mjs.map +1 -1
  24. package/dist/controllers/user-storage/contact-syncing/controller-integration.cjs +233 -166
  25. package/dist/controllers/user-storage/contact-syncing/controller-integration.cjs.map +1 -1
  26. package/dist/controllers/user-storage/contact-syncing/controller-integration.d.cts +3 -0
  27. package/dist/controllers/user-storage/contact-syncing/controller-integration.d.cts.map +1 -1
  28. package/dist/controllers/user-storage/contact-syncing/controller-integration.d.mts +3 -0
  29. package/dist/controllers/user-storage/contact-syncing/controller-integration.d.mts.map +1 -1
  30. package/dist/controllers/user-storage/contact-syncing/controller-integration.mjs +233 -166
  31. package/dist/controllers/user-storage/contact-syncing/controller-integration.mjs.map +1 -1
  32. package/dist/controllers/user-storage/contact-syncing/types.cjs.map +1 -1
  33. package/dist/controllers/user-storage/contact-syncing/types.d.cts +2 -0
  34. package/dist/controllers/user-storage/contact-syncing/types.d.cts.map +1 -1
  35. package/dist/controllers/user-storage/contact-syncing/types.d.mts +2 -0
  36. package/dist/controllers/user-storage/contact-syncing/types.d.mts.map +1 -1
  37. package/dist/controllers/user-storage/contact-syncing/types.mjs.map +1 -1
  38. package/package.json +1 -1
@@ -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 { 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"]}
1
+ {"version":3,"file":"controller-integration.mjs","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/controller-integration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,qCAAqC;AAG5D,OAAO,EACL,wBAAwB,EACxB,uBAAuB,EACvB,0BAA0B,EAC3B,yBAAqB;AAEtB,OAAO,EACL,wBAAwB,EACxB,sCAAsC,EACvC,oBAAgB;AACjB,OAAO,EAAE,0BAA0B,EAAE,2CAAuC;AAE5E;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,gCAAgC,CACpD,eAAgC,EAChC,OAA8B;IAE9B,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAE1B,MAAM,WAAW,GAAG,KAAK,IAAI,EAAE;QAC7B,MAAM,EAAE,gCAAgC,EAAE,GAAG,OAAO,CAAC;QAErD,IACE,CAAC,wBAAwB,CAAC,OAAO,CAAC;YAClC,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,8DAA8D;UAChI;YACA,OAAO;SACR;QAED,+IAA+I;QAC/I,0DAA0D;QAC1D,MAAM,eAAe,GAAG,eAAe,CAAC,OAAO,EAAE,aAEpC,CAAC;QAEd,IAAI;YACF,8DAA8D;YAC9D,MAAM,aAAa,GACjB,sCAAsC,CAAC,eAAe,CAAC,CAAC;YAE1D,MAAM,gCAAgC,EAAE,CAAC,iBAAiB,CACxD,GAAG,0BAA0B,CAAC,QAAQ,IAAI,eAAe,CAAC,OAAO,EAAE,EACnE,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,EAC7B,eAAe,CAChB,CAAC;SACH;QAAC,OAAO,CAAC,EAAE;YACV,uBAAuB;YACvB,MAAM,YAAY,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YACxE,MAAM,IAAI,KAAK,CACb,oEAAoE,YAAY,EAAE,CACnF,CAAC;SACH;IACH,CAAC,CAAC;IAEF,OAAO,KAAK;QACV,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,IAAI,EAAE,8BAA8B,EAAE,EAAE,WAAW,CAAC;QACpE,CAAC,CAAC,MAAM,WAAW,EAAE,CAAC;AAC1B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,qCAAqC,CACzD,OAA8B,EAC9B,eAAuB;IAEvB,MAAM,EAAE,gCAAgC,EAAE,GAAG,OAAO,CAAC;IAErD,MAAM,oBAAoB,GAAG,MAAM,uBAAuB,CACxD,OAAO,EACP,eAAe,CAChB,CAAC;IAEF,IAAI,CAAC,oBAAoB,EAAE,MAAM,EAAE;QACjC,OAAO;KACR;IAED,MAAM,2CAA2C,GAAG,oBAAoB,CAAC,GAAG,CAC1E,sCAAsC,CACvC,CAAC;IAEF,MAAM,gCAAgC,EAAE,CAAC,sBAAsB,CAC7D,0BAA0B,CAAC,QAAQ,EACnC,2CAA2C,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;QAC3D,OAAO,CAAC,CAAC;QACT,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;KACxB,CAAC,EACF,eAAe,CAChB,CAAC;AACJ,CAAC;AAYD;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,mCAAmC,CACvD,MAAiD,EACjD,OAA8B,EAC9B,eAAuB;IAEvB,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAE1B,MAAM,kBAAkB,GAAG,KAAK,IAAI,EAAE;QACpC,IAAI,CAAC,wBAAwB,CAAC,OAAO,CAAC,EAAE;YACtC,OAAO;SACR;QAED,MAAM,EACJ,wBAAwB,GAAG,QAAQ,EACnC,cAAc,EACd,oBAAoB,EACpB,+BAA+B,GAChC,GAAG,MAAM,CAAC;QACX,MAAM,EAAE,YAAY,EAAE,gCAAgC,EAAE,GAAG,OAAO,CAAC;QAEnE,IAAI;YACF,MAAM,gCAAgC,EAAE,CAAC,6BAA6B,CACpE,IAAI,CACL,CAAC;YAEF,MAAM,uBAAuB,GAAG,MAAM,0BAA0B,CAC9D,OAAO,EACP,eAAe,CAChB,CAAC;YAEF,IAAI,CAAC,uBAAuB,IAAI,CAAC,uBAAuB,CAAC,MAAM,EAAE;gBAC/D,MAAM,qCAAqC,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;gBACtE,OAAO;aACR;YACD,kEAAkE;YAClE,sFAAsF;YACtF,IAAI,wBAAwB,GAAG,KAAK,CAAC;YAErC,wEAAwE;YACxE,MAAM,sCAAsC,GAAsB,EAAE,CAAC;YAErE,iEAAiE;YACjE,8BAA8B;YAC9B,MAAM,oBAAoB,GAAG,MAAM,uBAAuB,CACxD,OAAO,EACP,eAAe,CAChB,CAAC;YAEF,IAAI,CAAC,oBAAoB,IAAI,CAAC,oBAAoB,CAAC,MAAM,EAAE;gBACzD,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;aACzD;YAED,MAAM,8CAA8C,GAClD,uBAAuB,CAAC,MAAM,GAAG,oBAAoB,CAAC,MAAM,CAAC;YAE/D,uDAAuD;YACvD,mGAAmG;YACnG,IAAI,8CAA8C,EAAE;gBAClD,MAAM,qBAAqB,GACzB,IAAI,CAAC,GAAG,CAAC,uBAAuB,CAAC,MAAM,EAAE,wBAAwB,CAAC;oBAClE,oBAAoB,CAAC,MAAM,CAAC;gBAE9B,8DAA8D;gBAC9D,MAAM,YAAY,EAAE,CAAC,IAAI,CACvB,+BAA+B,EAC/B;oBACE,EAAE,EAAE,eAAe;iBACpB,EACD,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;oBACpB,MAAM,OAAO,CAAC,WAAW,CAAC,qBAAqB,CAAC,CAAC;gBACnD,CAAC,CACF,CAAC;gBAEF,2EAA2E;gBAC3E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,qBAAqB,EAAE,CAAC,EAAE,EAAE;oBAC9C,cAAc,EAAE,EAAE,CAAC;iBACpB;aACF;YAED,qCAAqC;YACrC,qGAAqG;YACrG,MAAM,6BAA6B,GAAG,MAAM,uBAAuB,CACjE,OAAO,EACP,eAAe,CAChB,CAAC;YAEF,MAAM,kBAAkB,GAAG,6BAA6B,CAAC,MAAM,CAC7D,CAAC,OAAO,EAAE,EAAE,CACV,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CACnE,CAAC;YAEF,KAAK,MAAM,eAAe,IAAI,6BAA6B,EAAE;gBAC3D,MAAM,kBAAkB,GAAG,uBAAuB,CAAC,IAAI,CACrD,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,KAAK,eAAe,CAAC,OAAO,CACnD,CAAC;gBAEF,gDAAgD;gBAChD,uBAAuB;gBACvB,IAAI,CAAC,kBAAkB,EAAE;oBACvB,wGAAwG;oBACxG,IAAI,kBAAkB,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE;wBAChD,wBAAwB,GAAG,IAAI,CAAC;wBAChC,+BAA+B,EAAE,CAC/B,0GAA0G,EAC1G;4BACE,eAAe;4BACf,kBAAkB;4BAClB,kBAAkB;4BAClB,uBAAuB;4BACvB,oBAAoB;4BACpB,6BAA6B;4BAC7B,sCAAsC;yBACvC,CACF,CAAC;wBACF,SAAS;qBACV;oBACD,wHAAwH;oBACxH,sCAAsC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;oBAC7D,SAAS;iBACV;gBAED,6DAA6D;gBAC7D,qEAAqE;gBAErE,0CAA0C;gBAC1C,MAAM,4BAA4B,GAAG,wBAAwB,CAC3D,eAAe,CAAC,QAAQ,CAAC,IAAI,CAC9B,CAAC;gBACF,MAAM,+BAA+B,GAAG,wBAAwB,CAC9D,kBAAkB,CAAC,CAAC,CACrB,CAAC;gBAEF,oCAAoC;gBACpC,IAAI,4BAA4B,EAAE;oBAChC,IAAI,CAAC,+BAA+B,EAAE;wBACpC,YAAY,EAAE,CAAC,IAAI,CACjB,0CAA0C,EAC1C,eAAe,CAAC,EAAE,EAClB;4BACE,IAAI,EAAE,kBAAkB,CAAC,CAAC;yBAC3B,CACF,CAAC;wBAEF,oBAAoB,EAAE,EAAE,CAAC;qBAC1B;oBACD,SAAS;iBACV;gBAED,6EAA6E;gBAC7E,IAAI,+BAA+B,EAAE;oBACnC,sCAAsC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;oBAC7D,SAAS;iBACV;gBAED,kCAAkC;gBAElC,yDAAyD;gBACzD,6EAA6E;gBAC7E,IAAI,kBAAkB,CAAC,GAAG,KAAK,SAAS,EAAE;oBACxC,IAAI,eAAe,CAAC,QAAQ,CAAC,iBAAiB,KAAK,SAAS,EAAE;wBAC5D,MAAM,0BAA0B,GAC9B,eAAe,CAAC,QAAQ,CAAC,iBAAiB;4BAC1C,kBAAkB,CAAC,GAAG,CAAC;wBAEzB,IAAI,0BAA0B,EAAE;4BAC9B,sCAAsC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;4BAC7D,SAAS;yBACV;qBACF;oBAED,YAAY,EAAE,CAAC,IAAI,CACjB,0CAA0C,EAC1C,eAAe,CAAC,EAAE,EAClB;wBACE,IAAI,EAAE,kBAAkB,CAAC,CAAC;wBAC1B,iBAAiB,EAAE,kBAAkB,CAAC,GAAG;qBAC1C,CACF,CAAC;oBAEF,MAAM,0CAA0C,GAC9C,eAAe,CAAC,QAAQ,CAAC,IAAI,KAAK,kBAAkB,CAAC,CAAC,CAAC;oBAEzD,IAAI,CAAC,0CAA0C,EAAE;wBAC/C,oBAAoB,EAAE,EAAE,CAAC;qBAC1B;oBAED,SAAS;iBACV;qBAAM,IAAI,eAAe,CAAC,QAAQ,CAAC,iBAAiB,KAAK,SAAS,EAAE;oBACnE,sCAAsC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;oBAC7D,SAAS;iBACV;aACF;YAED,sDAAsD;YACtD,IAAI,sCAAsC,CAAC,MAAM,EAAE;gBACjD,MAAM,gCAAgC,EAAE,CAAC,sBAAsB,CAC7D,0BAA0B,CAAC,QAAQ,EACnC,sCAAsC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;oBACtD,OAAO,CAAC,OAAO;oBACf,IAAI,CAAC,SAAS,CAAC,sCAAsC,CAAC,OAAO,CAAC,CAAC;iBAChE,CAAC,EACF,eAAe,CAChB,CAAC;aACH;YAED,sGAAsG;YACtG,8CAA8C;YAC9C,MAAM,8BAA8B,GAAG,uBAAuB,CAAC,MAAM,CACnE,CAAC,OAAO,EAAE,EAAE,CACV,CAAC,6BAA6B,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,CACtE,CAAC;YAEF,IAAI,8BAA8B,CAAC,MAAM,EAAE;gBACzC,MAAM,gCAAgC,EAAE,CAAC,yBAAyB,CAChE,0BAA0B,CAAC,QAAQ,EACnC,8BAA8B,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,EAC1D,eAAe,CAChB,CAAC;gBACF,wBAAwB,GAAG,IAAI,CAAC;gBAChC,+BAA+B,EAAE,CAC/B,yHAAyH,EACzH;oBACE,8BAA8B;oBAC9B,oBAAoB;oBACpB,6BAA6B;oBAC7B,sCAAsC;oBACtC,uBAAuB;iBACxB,CACF,CAAC;aACH;YAED,IAAI,wBAAwB,EAAE;gBAC5B,MAAM,CAAC,4BAA4B,EAAE,yBAAyB,CAAC,GAC7D,MAAM,OAAO,CAAC,GAAG,CAAC;oBAChB,0BAA0B,CAAC,OAAO,EAAE,eAAe,CAAC;oBACpD,uBAAuB,CAAC,OAAO,EAAE,eAAe,CAAC;iBAClD,CAAC,CAAC;gBAEL,MAAM,oEAAoE,GACxE,yBAAyB,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,EAAE,CAC1C,4BAA4B,EAAE,IAAI,CAChC,CAAC,kBAAkB,EAAE,EAAE,CAAC,kBAAkB,CAAC,CAAC,KAAK,OAAO,CAAC,OAAO,CACjE,CACF,CAAC;gBAEJ,uBAAuB;gBACvB,MAAM,oEAAoE,GACxE,CAAC,4BAA4B,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,wBAAwB;oBACpE,CAAC,CAAC,IAAI;oBACN,CAAC,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC,OAAO,EAAE,EAAE,CAC9C,yBAAyB,CAAC,IAAI,CAC5B,CAAC,eAAe,EAAE,EAAE,CAAC,eAAe,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC,CAC3D,CACF,CAAC;gBAER,MAAM,iBAAiB,GACrB,oEAAoE;oBACpE,oEAAoE,CAAC;gBAEvE,MAAM,OAAO,GAAG;oBACd,4BAA4B;oBAC5B,yBAAyB;iBAC1B,CAAC;gBACF,IAAI,iBAAiB,EAAE;oBACrB,+BAA+B,EAAE,CAC/B,6FAA6F,EAC7F,OAAO,CACR,CAAC;iBACH;qBAAM;oBACL,+BAA+B,EAAE,CAC/B,oGAAoG,EACpG,OAAO,CACR,CAAC;iBACH;aACF;SACF;QAAC,OAAO,CAAC,EAAE;YACV,uBAAuB;YACvB,MAAM,YAAY,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YACxE,MAAM,IAAI,KAAK,CACb,uEAAuE,YAAY,EAAE,CACtF,CAAC;SACH;gBAAS;YACR,MAAM,gCAAgC,EAAE,CAAC,6BAA6B,CACpE,KAAK,CACN,CAAC;SACH;IACH,CAAC,CAAC;IAEF,OAAO,KAAK;QACV,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,EAAE,kBAAkB,CAAC;QAChE,CAAC,CAAC,MAAM,kBAAkB,EAAE,CAAC;AACjC,CAAC","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 * @returns Promise that resolves when the account is saved\n */\nexport async function saveInternalAccountToUserStorage(\n internalAccount: InternalAccount,\n options: AccountSyncingOptions,\n): Promise<void> {\n const { trace } = options;\n\n const saveAccount = async () => {\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 return trace\n ? await trace({ name: 'Account Sync Save Individual' }, saveAccount)\n : await saveAccount();\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 * @returns Promise that resolves when all accounts are saved\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 * @returns Promise that resolves when synchronization is complete\n */\nexport async function syncInternalAccountsWithUserStorage(\n config: SyncInternalAccountsWithUserStorageConfig,\n options: AccountSyncingOptions,\n entropySourceId: string,\n): Promise<void> {\n const { trace } = options;\n\n const performAccountSync = async () => {\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 >\n 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\n return trace\n ? await trace({ name: 'Account Sync Full' }, performAccountSync)\n : await performAccountSync();\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"types.cjs","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n USER_STORAGE_VERSION_KEY,\n USER_STORAGE_VERSION,\n} from './constants';\nimport type { UserStorageControllerMessenger } from '../UserStorageController';\nimport type UserStorageController from '../UserStorageController';\n\nexport type UserStorageAccount = {\n /**\n * The Version 'v' of the User Storage.\n * NOTE - will allow us to support upgrade/downgrades in the future\n */\n [USER_STORAGE_VERSION_KEY]: typeof USER_STORAGE_VERSION;\n /** the id 'i' of the account */\n i: string;\n /** the address 'a' of the account */\n a: string;\n /** the name 'n' of the account */\n n: string;\n /** the nameLastUpdatedAt timestamp 'nlu' of the account */\n nlu?: number;\n};\n\nexport type AccountSyncingOptions = {\n getUserStorageControllerInstance: () => UserStorageController;\n getMessenger: () => UserStorageControllerMessenger;\n};\n"]}
1
+ {"version":3,"file":"types.cjs","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { TraceCallback } from '@metamask/controller-utils';\n\nimport type {\n USER_STORAGE_VERSION_KEY,\n USER_STORAGE_VERSION,\n} from './constants';\nimport type { UserStorageControllerMessenger } from '../UserStorageController';\nimport type UserStorageController from '../UserStorageController';\n\nexport type UserStorageAccount = {\n /**\n * The Version 'v' of the User Storage.\n * NOTE - will allow us to support upgrade/downgrades in the future\n */\n [USER_STORAGE_VERSION_KEY]: typeof USER_STORAGE_VERSION;\n /** the id 'i' of the account */\n i: string;\n /** the address 'a' of the account */\n a: string;\n /** the name 'n' of the account */\n n: string;\n /** the nameLastUpdatedAt timestamp 'nlu' of the account */\n nlu?: number;\n};\n\nexport type AccountSyncingOptions = {\n getUserStorageControllerInstance: () => UserStorageController;\n getMessenger: () => UserStorageControllerMessenger;\n trace?: TraceCallback;\n};\n"]}
@@ -1,3 +1,4 @@
1
+ import type { TraceCallback } from "@metamask/controller-utils";
1
2
  import type { USER_STORAGE_VERSION_KEY, USER_STORAGE_VERSION } from "./constants.cjs";
2
3
  import type { UserStorageControllerMessenger } from "../UserStorageController.cjs";
3
4
  import type UserStorageController from "../UserStorageController.cjs";
@@ -19,5 +20,6 @@ export type UserStorageAccount = {
19
20
  export type AccountSyncingOptions = {
20
21
  getUserStorageControllerInstance: () => UserStorageController;
21
22
  getMessenger: () => UserStorageControllerMessenger;
23
+ trace?: TraceCallback;
22
24
  };
23
25
  //# sourceMappingURL=types.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.cts","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,oBAAoB,EACrB,wBAAoB;AACrB,OAAO,KAAK,EAAE,8BAA8B,EAAE,qCAAiC;AAC/E,OAAO,KAAK,qBAAqB,qCAAiC;AAElE,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;;OAGG;IACH,CAAC,wBAAwB,CAAC,EAAE,OAAO,oBAAoB,CAAC;IACxD,gCAAgC;IAChC,CAAC,EAAE,MAAM,CAAC;IACV,qCAAqC;IACrC,CAAC,EAAE,MAAM,CAAC;IACV,kCAAkC;IAClC,CAAC,EAAE,MAAM,CAAC;IACV,2DAA2D;IAC3D,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,gCAAgC,EAAE,MAAM,qBAAqB,CAAC;IAC9D,YAAY,EAAE,MAAM,8BAA8B,CAAC;CACpD,CAAC"}
1
+ {"version":3,"file":"types.d.cts","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,mCAAmC;AAEhE,OAAO,KAAK,EACV,wBAAwB,EACxB,oBAAoB,EACrB,wBAAoB;AACrB,OAAO,KAAK,EAAE,8BAA8B,EAAE,qCAAiC;AAC/E,OAAO,KAAK,qBAAqB,qCAAiC;AAElE,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;;OAGG;IACH,CAAC,wBAAwB,CAAC,EAAE,OAAO,oBAAoB,CAAC;IACxD,gCAAgC;IAChC,CAAC,EAAE,MAAM,CAAC;IACV,qCAAqC;IACrC,CAAC,EAAE,MAAM,CAAC;IACV,kCAAkC;IAClC,CAAC,EAAE,MAAM,CAAC;IACV,2DAA2D;IAC3D,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,gCAAgC,EAAE,MAAM,qBAAqB,CAAC;IAC9D,YAAY,EAAE,MAAM,8BAA8B,CAAC;IACnD,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB,CAAC"}
@@ -1,3 +1,4 @@
1
+ import type { TraceCallback } from "@metamask/controller-utils";
1
2
  import type { USER_STORAGE_VERSION_KEY, USER_STORAGE_VERSION } from "./constants.mjs";
2
3
  import type { UserStorageControllerMessenger } from "../UserStorageController.mjs";
3
4
  import type UserStorageController from "../UserStorageController.mjs";
@@ -19,5 +20,6 @@ export type UserStorageAccount = {
19
20
  export type AccountSyncingOptions = {
20
21
  getUserStorageControllerInstance: () => UserStorageController;
21
22
  getMessenger: () => UserStorageControllerMessenger;
23
+ trace?: TraceCallback;
22
24
  };
23
25
  //# sourceMappingURL=types.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.mts","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,oBAAoB,EACrB,wBAAoB;AACrB,OAAO,KAAK,EAAE,8BAA8B,EAAE,qCAAiC;AAC/E,OAAO,KAAK,qBAAqB,qCAAiC;AAElE,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;;OAGG;IACH,CAAC,wBAAwB,CAAC,EAAE,OAAO,oBAAoB,CAAC;IACxD,gCAAgC;IAChC,CAAC,EAAE,MAAM,CAAC;IACV,qCAAqC;IACrC,CAAC,EAAE,MAAM,CAAC;IACV,kCAAkC;IAClC,CAAC,EAAE,MAAM,CAAC;IACV,2DAA2D;IAC3D,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,gCAAgC,EAAE,MAAM,qBAAqB,CAAC;IAC9D,YAAY,EAAE,MAAM,8BAA8B,CAAC;CACpD,CAAC"}
1
+ {"version":3,"file":"types.d.mts","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,mCAAmC;AAEhE,OAAO,KAAK,EACV,wBAAwB,EACxB,oBAAoB,EACrB,wBAAoB;AACrB,OAAO,KAAK,EAAE,8BAA8B,EAAE,qCAAiC;AAC/E,OAAO,KAAK,qBAAqB,qCAAiC;AAElE,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;;OAGG;IACH,CAAC,wBAAwB,CAAC,EAAE,OAAO,oBAAoB,CAAC;IACxD,gCAAgC;IAChC,CAAC,EAAE,MAAM,CAAC;IACV,qCAAqC;IACrC,CAAC,EAAE,MAAM,CAAC;IACV,kCAAkC;IAClC,CAAC,EAAE,MAAM,CAAC;IACV,2DAA2D;IAC3D,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,gCAAgC,EAAE,MAAM,qBAAqB,CAAC;IAC9D,YAAY,EAAE,MAAM,8BAA8B,CAAC;IACnD,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.mjs","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n USER_STORAGE_VERSION_KEY,\n USER_STORAGE_VERSION,\n} from './constants';\nimport type { UserStorageControllerMessenger } from '../UserStorageController';\nimport type UserStorageController from '../UserStorageController';\n\nexport type UserStorageAccount = {\n /**\n * The Version 'v' of the User Storage.\n * NOTE - will allow us to support upgrade/downgrades in the future\n */\n [USER_STORAGE_VERSION_KEY]: typeof USER_STORAGE_VERSION;\n /** the id 'i' of the account */\n i: string;\n /** the address 'a' of the account */\n a: string;\n /** the name 'n' of the account */\n n: string;\n /** the nameLastUpdatedAt timestamp 'nlu' of the account */\n nlu?: number;\n};\n\nexport type AccountSyncingOptions = {\n getUserStorageControllerInstance: () => UserStorageController;\n getMessenger: () => UserStorageControllerMessenger;\n};\n"]}
1
+ {"version":3,"file":"types.mjs","sourceRoot":"","sources":["../../../../src/controllers/user-storage/account-syncing/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { TraceCallback } from '@metamask/controller-utils';\n\nimport type {\n USER_STORAGE_VERSION_KEY,\n USER_STORAGE_VERSION,\n} from './constants';\nimport type { UserStorageControllerMessenger } from '../UserStorageController';\nimport type UserStorageController from '../UserStorageController';\n\nexport type UserStorageAccount = {\n /**\n * The Version 'v' of the User Storage.\n * NOTE - will allow us to support upgrade/downgrades in the future\n */\n [USER_STORAGE_VERSION_KEY]: typeof USER_STORAGE_VERSION;\n /** the id 'i' of the account */\n i: string;\n /** the address 'a' of the account */\n a: string;\n /** the name 'n' of the account */\n n: string;\n /** the nameLastUpdatedAt timestamp 'nlu' of the account */\n nlu?: number;\n};\n\nexport type AccountSyncingOptions = {\n getUserStorageControllerInstance: () => UserStorageController;\n getMessenger: () => UserStorageControllerMessenger;\n trace?: TraceCallback;\n};\n"]}
@@ -35,138 +35,167 @@ function createContactKey(contact) {
35
35
  *
36
36
  * @param config - Parameters used for syncing callbacks
37
37
  * @param options - Parameters used for syncing operations
38
+ * @returns Promise that resolves when contact synchronization is complete
38
39
  */
39
40
  async function syncContactsWithUserStorage(config, options) {
40
- const { getMessenger, getUserStorageControllerInstance } = options;
41
+ const { getMessenger, getUserStorageControllerInstance, trace } = options;
41
42
  const { onContactSyncErroneousSituation, onContactUpdated, onContactDeleted, } = config;
42
- try {
43
- // Cannot perform sync, conditions not met
44
- if (!(0, sync_utils_1.canPerformContactSyncing)(options)) {
45
- return;
46
- }
47
- // Activate sync semaphore to prevent event loops
48
- await getUserStorageControllerInstance().setIsContactSyncingInProgress(true);
49
- // Get all local contacts from AddressBookController (exclude chain "*" contacts)
50
- const localVisibleContacts = getMessenger()
51
- .call('AddressBookController:list')
52
- .filter((contact) => !(0, utils_2.isContactBridgedFromAccounts)(contact))
53
- .filter((contact) => contact.address && contact.chainId && contact.name?.trim()) || [];
54
- // Get remote contacts from user storage API
55
- const remoteContacts = await getRemoteContacts(options);
56
- // Filter remote contacts to exclude invalid ones (or empty array if no remote contacts)
57
- const validRemoteContacts = remoteContacts?.filter((contact) => contact.address && contact.chainId && contact.name?.trim()) || [];
58
- // Prepare maps for efficient lookup
59
- const localContactsMap = new Map();
60
- const remoteContactsMap = new Map();
61
- localVisibleContacts.forEach((contact) => {
62
- const key = createContactKey(contact);
63
- localContactsMap.set(key, contact);
64
- });
65
- validRemoteContacts.forEach((contact) => {
66
- const key = createContactKey(contact);
67
- remoteContactsMap.set(key, contact);
68
- });
69
- // Lists to track contacts that need to be synced
70
- const contactsToAddOrUpdateLocally = [];
71
- const contactsToDeleteLocally = [];
72
- const contactsToUpdateRemotely = [];
73
- // SCENARIO 2 & 6: Process remote contacts - handle new device sync and remote updates
74
- for (const remoteContact of validRemoteContacts) {
75
- const key = createContactKey(remoteContact);
76
- const localContact = localContactsMap.get(key);
77
- // Handle remote contact based on its status and local existence
78
- if (remoteContact.deletedAt) {
79
- // SCENARIO 8: Remote deletion - should be applied locally if contact exists locally
80
- if (localContact) {
81
- contactsToDeleteLocally.push(remoteContact);
82
- }
43
+ let localVisibleContacts = [];
44
+ let validRemoteContacts = [];
45
+ const performSync = async () => {
46
+ try {
47
+ // Cannot perform sync, conditions not met
48
+ if (!(0, sync_utils_1.canPerformContactSyncing)(options)) {
49
+ return;
83
50
  }
84
- else if (!localContact) {
85
- // SCENARIO 2: New contact from remote - import to local
86
- contactsToAddOrUpdateLocally.push(remoteContact);
87
- }
88
- else {
89
- // SCENARIO 4 & 6: Contact exists on both sides - check for conflicts
90
- const hasContentDifference = localContact.name !== remoteContact.name ||
91
- localContact.memo !== remoteContact.memo;
92
- if (hasContentDifference) {
93
- // Check timestamps to determine which version to keep
94
- const localTimestamp = localContact.lastUpdatedAt || 0;
95
- const remoteTimestamp = remoteContact.lastUpdatedAt || 0;
96
- if (localTimestamp >= remoteTimestamp) {
97
- // Local is newer (or same age) - use local version
98
- contactsToUpdateRemotely.push(localContact);
51
+ // Activate sync semaphore to prevent event loops
52
+ await getUserStorageControllerInstance().setIsContactSyncingInProgress(true);
53
+ // Get all local contacts from AddressBookController (exclude chain "*" contacts)
54
+ localVisibleContacts =
55
+ getMessenger()
56
+ .call('AddressBookController:list')
57
+ .filter((contact) => !(0, utils_2.isContactBridgedFromAccounts)(contact))
58
+ .filter((contact) => contact.address && contact.chainId && contact.name?.trim()) || [];
59
+ // Get remote contacts from user storage API
60
+ const remoteContacts = await getRemoteContacts(options);
61
+ // Filter remote contacts to exclude invalid ones (or empty array if no remote contacts)
62
+ validRemoteContacts =
63
+ remoteContacts?.filter((contact) => contact.address && contact.chainId && contact.name?.trim()) || [];
64
+ // Prepare maps for efficient lookup
65
+ const localContactsMap = new Map();
66
+ const remoteContactsMap = new Map();
67
+ localVisibleContacts.forEach((contact) => {
68
+ const key = createContactKey(contact);
69
+ localContactsMap.set(key, contact);
70
+ });
71
+ validRemoteContacts.forEach((contact) => {
72
+ const key = createContactKey(contact);
73
+ remoteContactsMap.set(key, contact);
74
+ });
75
+ // Lists to track contacts that need to be synced
76
+ const contactsToAddOrUpdateLocally = [];
77
+ const contactsToDeleteLocally = [];
78
+ const contactsToUpdateRemotely = [];
79
+ // SCENARIO 2 & 6: Process remote contacts - handle new device sync and remote updates
80
+ for (const remoteContact of validRemoteContacts) {
81
+ const key = createContactKey(remoteContact);
82
+ const localContact = localContactsMap.get(key);
83
+ // Handle remote contact based on its status and local existence
84
+ if (remoteContact.deletedAt) {
85
+ // SCENARIO 8: Remote deletion - should be applied locally if contact exists locally
86
+ if (localContact) {
87
+ contactsToDeleteLocally.push(remoteContact);
99
88
  }
100
- else {
101
- // Remote is newer - use remote version
102
- contactsToAddOrUpdateLocally.push(remoteContact);
89
+ }
90
+ else if (!localContact) {
91
+ // SCENARIO 2: New contact from remote - import to local
92
+ contactsToAddOrUpdateLocally.push(remoteContact);
93
+ }
94
+ else {
95
+ // SCENARIO 4 & 6: Contact exists on both sides - check for conflicts
96
+ const hasContentDifference = localContact.name !== remoteContact.name ||
97
+ localContact.memo !== remoteContact.memo;
98
+ if (hasContentDifference) {
99
+ // Check timestamps to determine which version to keep
100
+ const localTimestamp = localContact.lastUpdatedAt || 0;
101
+ const remoteTimestamp = remoteContact.lastUpdatedAt || 0;
102
+ if (localTimestamp >= remoteTimestamp) {
103
+ // Local is newer (or same age) - use local version
104
+ contactsToUpdateRemotely.push(localContact);
105
+ }
106
+ else {
107
+ // Remote is newer - use remote version
108
+ contactsToAddOrUpdateLocally.push(remoteContact);
109
+ }
103
110
  }
111
+ // Else: content is identical, no action needed
104
112
  }
105
- // Else: content is identical, no action needed
106
- }
107
- }
108
- // SCENARIO 1, 3 & 5: Process local contacts not in remote - handles first sync and new local contacts
109
- for (const localContact of localVisibleContacts) {
110
- const key = createContactKey(localContact);
111
- const remoteContact = remoteContactsMap.get(key);
112
- if (!remoteContact) {
113
- // New local contact or first sync - add to remote
114
- contactsToUpdateRemotely.push(localContact);
115
113
  }
116
- }
117
- // Apply local deletions
118
- for (const contact of contactsToDeleteLocally) {
119
- try {
120
- getMessenger().call('AddressBookController:delete', contact.chainId, contact.address);
121
- if (onContactDeleted) {
122
- onContactDeleted();
114
+ // SCENARIO 1, 3 & 5: Process local contacts not in remote - handles first sync and new local contacts
115
+ for (const localContact of localVisibleContacts) {
116
+ const key = createContactKey(localContact);
117
+ const remoteContact = remoteContactsMap.get(key);
118
+ if (!remoteContact) {
119
+ // New local contact or first sync - add to remote
120
+ contactsToUpdateRemotely.push(localContact);
123
121
  }
124
122
  }
125
- catch (error) {
126
- console.error('Error deleting contact:', error);
127
- }
128
- }
129
- // Apply local additions/updates
130
- for (const contact of contactsToAddOrUpdateLocally) {
131
- if (!contact.deletedAt) {
123
+ // Apply local deletions
124
+ for (const contact of contactsToDeleteLocally) {
132
125
  try {
133
- getMessenger().call('AddressBookController:set', contact.address, contact.name || '', contact.chainId, contact.memo || '', contact.addressType);
134
- if (onContactUpdated) {
135
- onContactUpdated();
126
+ getMessenger().call('AddressBookController:delete', contact.chainId, contact.address);
127
+ if (onContactDeleted) {
128
+ onContactDeleted();
136
129
  }
137
130
  }
138
131
  catch (error) {
139
- console.error('Error updating contact:', error);
132
+ console.error('Error deleting contact:', error);
133
+ }
134
+ }
135
+ // Apply local additions/updates
136
+ for (const contact of contactsToAddOrUpdateLocally) {
137
+ if (!contact.deletedAt) {
138
+ try {
139
+ getMessenger().call('AddressBookController:set', contact.address, contact.name || '', contact.chainId, contact.memo || '', contact.addressType);
140
+ if (onContactUpdated) {
141
+ onContactUpdated();
142
+ }
143
+ }
144
+ catch (error) {
145
+ console.error('Error updating contact:', error);
146
+ }
147
+ }
148
+ }
149
+ // Apply changes to remote storage
150
+ if (contactsToUpdateRemotely.length > 0) {
151
+ const updatedRemoteContacts = {};
152
+ for (const localContact of contactsToUpdateRemotely) {
153
+ const key = createContactKey(localContact);
154
+ updatedRemoteContacts[key] = {
155
+ ...remoteContactsMap.get(key),
156
+ ...localContact,
157
+ lastUpdatedAt: Date.now(), // mark as updated
158
+ };
140
159
  }
160
+ // Save updated contacts to remote storage
161
+ await saveContactsToUserStorage(Object.values(updatedRemoteContacts), options);
141
162
  }
142
163
  }
143
- // Apply changes to remote storage
144
- if (contactsToUpdateRemotely.length > 0) {
145
- const updatedRemoteContacts = {};
146
- for (const localContact of contactsToUpdateRemotely) {
147
- const key = createContactKey(localContact);
148
- updatedRemoteContacts[key] = {
149
- ...remoteContactsMap.get(key),
150
- ...localContact,
151
- lastUpdatedAt: Date.now(), // mark as updated
152
- };
164
+ catch (error) {
165
+ if (onContactSyncErroneousSituation) {
166
+ onContactSyncErroneousSituation('Error synchronizing contacts', {
167
+ error,
168
+ });
169
+ // Re-throw the error to be handled by the caller
170
+ throw error;
153
171
  }
154
- // Save updated contacts to remote storage
155
- await saveContactsToUserStorage(Object.values(updatedRemoteContacts), options);
156
172
  }
157
- }
158
- catch (error) {
159
- if (onContactSyncErroneousSituation) {
160
- onContactSyncErroneousSituation('Error synchronizing contacts', {
161
- error,
162
- });
163
- // Re-throw the error to be handled by the caller
164
- throw error;
173
+ finally {
174
+ await getUserStorageControllerInstance().setIsContactSyncingInProgress(false);
165
175
  }
176
+ };
177
+ if (trace) {
178
+ // Gather pre-sync metrics for performance analysis
179
+ const initialLocalContacts = localVisibleContacts;
180
+ const initialValidRemoteContacts = validRemoteContacts;
181
+ return await trace({
182
+ name: 'Contact Sync Full',
183
+ data: {
184
+ localContactCount: initialLocalContacts.length,
185
+ remoteContactCount: initialValidRemoteContacts.length,
186
+ isFirstSync: initialValidRemoteContacts.length === 0 &&
187
+ initialLocalContacts.length > 0,
188
+ isNewDeviceSync: initialLocalContacts.length === 0 &&
189
+ initialValidRemoteContacts.length > 0,
190
+ isRegularSync: initialLocalContacts.length > 0 &&
191
+ initialValidRemoteContacts.length > 0,
192
+ hasDataToSync: initialLocalContacts.length > 0 ||
193
+ initialValidRemoteContacts.length > 0,
194
+ expectedWorkload: initialLocalContacts.length + initialValidRemoteContacts.length,
195
+ },
196
+ }, performSync);
166
197
  }
167
- finally {
168
- await getUserStorageControllerInstance().setIsContactSyncingInProgress(false);
169
- }
198
+ return await performSync();
170
199
  }
171
200
  exports.syncContactsWithUserStorage = syncContactsWithUserStorage;
172
201
  /**
@@ -198,19 +227,34 @@ async function getRemoteContacts(options) {
198
227
  *
199
228
  * @param contacts - The contacts to save to user storage
200
229
  * @param options - Parameters used for saving contacts
230
+ * @returns Promise that resolves when contacts are saved
201
231
  */
202
232
  async function saveContactsToUserStorage(contacts, options) {
203
- const { getUserStorageControllerInstance } = options;
204
- if (!contacts || contacts.length === 0) {
205
- return;
206
- }
207
- // Convert each AddressBookEntry to UserStorageContactEntry format and create key-value pairs
208
- const storageEntries = contacts.map((contact) => {
209
- const key = createContactKey(contact);
210
- const storageEntry = (0, utils_1.mapAddressBookEntryToUserStorageEntry)(contact);
211
- return [key, JSON.stringify(storageEntry)];
212
- });
213
- await getUserStorageControllerInstance().performBatchSetStorage(storage_schema_1.USER_STORAGE_FEATURE_NAMES.addressBook, storageEntries);
233
+ const { getUserStorageControllerInstance, trace } = options;
234
+ const saveContacts = async () => {
235
+ if (!contacts || contacts.length === 0) {
236
+ return;
237
+ }
238
+ // Convert each AddressBookEntry to UserStorageContactEntry format and create key-value pairs
239
+ const storageEntries = contacts.map((contact) => {
240
+ const key = createContactKey(contact);
241
+ const storageEntry = (0, utils_1.mapAddressBookEntryToUserStorageEntry)(contact);
242
+ return [key, JSON.stringify(storageEntry)];
243
+ });
244
+ await getUserStorageControllerInstance().performBatchSetStorage(storage_schema_1.USER_STORAGE_FEATURE_NAMES.addressBook, storageEntries);
245
+ };
246
+ return trace
247
+ ? await trace({
248
+ name: 'Contact Sync Save Batch',
249
+ data: {
250
+ contactCount: contacts.length,
251
+ // Performance scaling indicators
252
+ hasBatchOperations: contacts.length > 1,
253
+ chainCount: new Set(contacts.map((c) => c.chainId)).size,
254
+ hasMemosCount: contacts.filter((c) => c.memo?.length).length,
255
+ },
256
+ }, saveContacts)
257
+ : await saveContacts();
214
258
  }
215
259
  /**
216
260
  * Updates a single contact in remote storage without performing a full sync
@@ -218,24 +262,40 @@ async function saveContactsToUserStorage(contacts, options) {
218
262
  *
219
263
  * @param contact - The contact that was updated locally
220
264
  * @param options - Parameters used for syncing operations
265
+ * @returns Promise that resolves when the contact is updated
221
266
  */
222
267
  async function updateContactInRemoteStorage(contact, options) {
223
- if (!(0, sync_utils_1.canPerformContactSyncing)(options) ||
224
- !contact.address ||
225
- !contact.chainId ||
226
- !contact.name?.trim()) {
227
- return;
228
- }
229
- const { getUserStorageControllerInstance } = options;
230
- // Create an updated entry with timestamp
231
- const updatedEntry = {
232
- ...contact,
233
- lastUpdatedAt: contact.lastUpdatedAt || Date.now(),
268
+ const { trace } = options;
269
+ const updateContact = async () => {
270
+ if (!(0, sync_utils_1.canPerformContactSyncing)(options) ||
271
+ !contact.address ||
272
+ !contact.chainId ||
273
+ !contact.name?.trim()) {
274
+ return;
275
+ }
276
+ const { getUserStorageControllerInstance } = options;
277
+ // Create an updated entry with timestamp
278
+ const updatedEntry = {
279
+ ...contact,
280
+ lastUpdatedAt: contact.lastUpdatedAt || Date.now(),
281
+ };
282
+ const key = createContactKey(contact);
283
+ const storageEntry = (0, utils_1.mapAddressBookEntryToUserStorageEntry)(updatedEntry);
284
+ // Save individual contact to remote storage
285
+ await getUserStorageControllerInstance().performSetStorage(`${storage_schema_1.USER_STORAGE_FEATURE_NAMES.addressBook}.${key}`, JSON.stringify(storageEntry));
234
286
  };
235
- const key = createContactKey(contact);
236
- const storageEntry = (0, utils_1.mapAddressBookEntryToUserStorageEntry)(updatedEntry);
237
- // Save individual contact to remote storage
238
- await getUserStorageControllerInstance().performSetStorage(`${storage_schema_1.USER_STORAGE_FEATURE_NAMES.addressBook}.${key}`, JSON.stringify(storageEntry));
287
+ return trace
288
+ ? await trace({
289
+ name: 'Contact Sync Update Remote',
290
+ data: {
291
+ chainId: contact.chainId,
292
+ // Performance indicators
293
+ hasTimestamp: Boolean(contact.lastUpdatedAt),
294
+ hasMemo: Boolean(contact.memo?.length),
295
+ isUpdate: Boolean(contact.lastUpdatedAt), // vs new contact
296
+ },
297
+ }, updateContact)
298
+ : await updateContact();
239
299
  }
240
300
  exports.updateContactInRemoteStorage = updateContactInRemoteStorage;
241
301
  /**
@@ -244,38 +304,45 @@ exports.updateContactInRemoteStorage = updateContactInRemoteStorage;
244
304
  *
245
305
  * @param contact - The contact that was deleted locally (contains at least address and chainId)
246
306
  * @param options - Parameters used for syncing operations
307
+ * @returns Promise that resolves when the contact is marked as deleted
247
308
  */
248
309
  async function deleteContactInRemoteStorage(contact, options) {
249
- if (!(0, sync_utils_1.canPerformContactSyncing)(options) ||
250
- !contact.address ||
251
- !contact.chainId ||
252
- !contact.name?.trim()) {
253
- return;
254
- }
255
- const { getUserStorageControllerInstance } = options;
256
- const key = createContactKey(contact);
257
- try {
258
- // Try to get the existing contact first
259
- const existingContactJson = await getUserStorageControllerInstance().performGetStorage(`${storage_schema_1.USER_STORAGE_FEATURE_NAMES.addressBook}.${key}`);
260
- if (existingContactJson) {
261
- // Mark the existing contact as deleted
262
- const existingStorageEntry = JSON.parse(existingContactJson);
263
- const existingContact = (0, utils_1.mapUserStorageEntryToAddressBookEntry)(existingStorageEntry);
264
- const now = Date.now();
265
- const deletedContact = {
266
- ...existingContact,
267
- deletedAt: now,
268
- lastUpdatedAt: now,
269
- };
270
- const deletedStorageEntry = (0, utils_1.mapAddressBookEntryToUserStorageEntry)(deletedContact);
271
- // Save the deleted contact back to storage
272
- await getUserStorageControllerInstance().performSetStorage(`${storage_schema_1.USER_STORAGE_FEATURE_NAMES.addressBook}.${key}`, JSON.stringify(deletedStorageEntry));
310
+ const { trace } = options;
311
+ const deleteContact = async () => {
312
+ if (!(0, sync_utils_1.canPerformContactSyncing)(options) ||
313
+ !contact.address ||
314
+ !contact.chainId ||
315
+ !contact.name?.trim()) {
316
+ return;
273
317
  }
274
- }
275
- catch {
276
- // If contact doesn't exist in remote storage, no need to mark as deleted
277
- console.warn('Contact not found in remote storage for deletion:', key);
278
- }
318
+ const { getUserStorageControllerInstance } = options;
319
+ const key = createContactKey(contact);
320
+ try {
321
+ // Try to get the existing contact first
322
+ const existingContactJson = await getUserStorageControllerInstance().performGetStorage(`${storage_schema_1.USER_STORAGE_FEATURE_NAMES.addressBook}.${key}`);
323
+ if (existingContactJson) {
324
+ // Mark the existing contact as deleted
325
+ const existingStorageEntry = JSON.parse(existingContactJson);
326
+ const existingContact = (0, utils_1.mapUserStorageEntryToAddressBookEntry)(existingStorageEntry);
327
+ const now = Date.now();
328
+ const deletedContact = {
329
+ ...existingContact,
330
+ deletedAt: now,
331
+ lastUpdatedAt: now,
332
+ };
333
+ const deletedStorageEntry = (0, utils_1.mapAddressBookEntryToUserStorageEntry)(deletedContact);
334
+ // Save the deleted contact back to storage
335
+ await getUserStorageControllerInstance().performSetStorage(`${storage_schema_1.USER_STORAGE_FEATURE_NAMES.addressBook}.${key}`, JSON.stringify(deletedStorageEntry));
336
+ }
337
+ }
338
+ catch {
339
+ // If contact doesn't exist in remote storage, no need to mark as deleted
340
+ console.warn('Contact not found in remote storage for deletion:', key);
341
+ }
342
+ };
343
+ return trace
344
+ ? await trace({ name: 'Contact Sync Delete Remote' }, deleteContact)
345
+ : await deleteContact();
279
346
  }
280
347
  exports.deleteContactInRemoteStorage = deleteContactInRemoteStorage;
281
348
  //# sourceMappingURL=controller-integration.cjs.map