@metamask-previews/account-tree-controller 8.1.0-preview-d8fceeabc → 8.1.0-preview-efd9e28b2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,16 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ### Added
11
-
12
- - Add `AccountTreeSnapshot:strip{Metadata,Secrets}` support ([#10112](https://github.com/MetaMask/core/pull/10112))
13
- - This can be used to mutate the snapshot before calling `:importState` and ease custom flow integration where secrets and metadata would be imported in 2-steps.
14
-
15
- ### Changed
16
-
17
- - **BREAKING:** `metadata` is now optional on `AccountWalletPayloadMetadata`, `AccountWalletGroupPayloadMetadata`, and all wallet/group payload entry types ([#10112](https://github.com/MetaMask/core/pull/10112))
18
- - Consumers reading `metadata` from a snapshot or payload must now guard against `undefined` (e.g. after calling `AccountTreeSnapshot.stripMetadata()`).
19
-
20
10
  ## [8.1.0]
21
11
 
22
12
  ### Added
@@ -92,11 +92,9 @@ function hasLocalGroupPrivateKey(localWallet, group) {
92
92
  * @param payloadGroupMetadata - Metadata from the payload group entry.
93
93
  */
94
94
  function setGroupMetadata(context, localGroupId, payloadGroupMetadata) {
95
- if (payloadGroupMetadata) {
96
- context.setAccountGroupName(localGroupId, payloadGroupMetadata.name);
97
- context.setAccountGroupPinned(localGroupId, payloadGroupMetadata.pinned);
98
- context.setAccountGroupHidden(localGroupId, payloadGroupMetadata.hidden);
99
- }
95
+ context.setAccountGroupName(localGroupId, payloadGroupMetadata.name);
96
+ context.setAccountGroupPinned(localGroupId, payloadGroupMetadata.pinned);
97
+ context.setAccountGroupHidden(localGroupId, payloadGroupMetadata.hidden);
100
98
  }
101
99
  /**
102
100
  * Computes the contiguous ranges of group indices that are present in the payload
@@ -154,9 +152,7 @@ async function importMnemonicWallet(context, payloadWallet) {
154
152
  // Event handlers fire synchronously, so the wallet is in the tree now.
155
153
  localWallet = findLocalWalletMnemonicFromId(context, id);
156
154
  }
157
- if (payloadWallet.metadata) {
158
- context.setWalletName(localWallet.id, payloadWallet.metadata.name);
159
- }
155
+ context.setWalletName(localWallet.id, payloadWallet.metadata.name);
160
156
  // Apply metadata to groups that are already present locally before attempting
161
157
  // to create missing ones. If createMultichainAccountGroups throws partway
162
158
  // through, these groups would otherwise be left without their payload metadata.
@@ -1 +1 @@
1
- {"version":3,"file":"import.cjs","sourceRoot":"","sources":["../../src/state/import.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n AccountWalletType,\n toAccountGroupId,\n toAccountWalletId,\n toMultichainAccountGroupId,\n} from '@metamask/account-api';\nimport type { AccountGroupId, AccountWalletId } from '@metamask/account-api';\nimport { getUUIDFromAddressOfNormalAccount } from '@metamask/accounts-controller';\nimport { HdKeyring } from '@metamask/eth-hd-keyring/v2';\nimport { EthAccountType } from '@metamask/keyring-api';\nimport { KeyringTypes } from '@metamask/keyring-controller';\n\nimport type {\n AccountTreeControllerMessenger,\n AccountTreeControllerState,\n} from '../types.js';\nimport type {\n AccountWalletEntropyObject,\n AccountWalletKeyringObject,\n} from '../wallet.js';\nimport { isMnemonicWalletObject } from './export.js';\nimport type {\n AccountWalletMnemonicGroupEntry,\n AccountWalletMnemonicPayload,\n AccountWalletPayloadId,\n AccountWalletPrivateKeyGroupEntry,\n} from './payload.js';\nimport {\n AccountWalletPayloadType,\n parsePayloadGroupId,\n toWalletPayloadId,\n} from './payload.js';\nimport type { AccountTreeSnapshot } from './snapshot.js';\nimport { decodeBytes } from './utils.js';\n\n/** Context required by {@link importState}. */\nexport type ImportContext = {\n getState: () => AccountTreeControllerState;\n messenger: AccountTreeControllerMessenger;\n setWalletName: (walletId: AccountWalletId, name: string) => void;\n /** Sets a group name. Implementations must resolve name conflicts automatically. */\n setAccountGroupName: (groupId: AccountGroupId, name: string) => void;\n setAccountGroupPinned: (groupId: AccountGroupId, pinned: boolean) => void;\n setAccountGroupHidden: (groupId: AccountGroupId, hidden: boolean) => void;\n};\n\n/**\n * Searches the local wallet tree for an entropy wallet whose stable payload ID\n * matches `payloadWalletId`. The entropy source ID is derived on-the-fly via\n * `KeyringController:withKeyringV2Unsafe` rather than relying on cached metadata.\n *\n * @param context - Import context.\n * @param payloadWalletId - Payload wallet ID to match against.\n * @returns The matching local entropy wallet, or `undefined` if not found.\n */\nasync function findLocalWalletMnemonicFromPayloadId(\n context: ImportContext,\n payloadWalletId: AccountWalletPayloadId,\n): Promise<AccountWalletEntropyObject | undefined> {\n const wallets = Object.values(context.getState().accountTree.wallets);\n\n for (const wallet of wallets) {\n if (isMnemonicWalletObject(wallet)) {\n const result = await context.messenger.call(\n 'KeyringController:withKeyringV2Unsafe',\n { id: wallet.metadata.entropy.id },\n async ({ keyring }) => {\n const hdKeyring = keyring as HdKeyring;\n\n return toWalletPayloadId(await hdKeyring.toEntropySourceId());\n },\n );\n\n const localPayloadId = result as AccountWalletPayloadId;\n if (localPayloadId === payloadWalletId) {\n return wallet;\n }\n }\n }\n\n return undefined;\n}\n\n/**\n * Returns the local entropy wallet for the given ID.\n *\n * @param context - Import context.\n * @param id - Local wallet ID.\n * @returns The entropy wallet object.\n * @throws If the wallet is not found or is not an entropy wallet.\n */\nfunction findLocalWalletMnemonicFromId(\n context: ImportContext,\n id: AccountWalletId,\n): AccountWalletEntropyObject {\n const localWallets = context.getState().accountTree.wallets;\n const localWallet = localWallets[id];\n\n if (!localWallet) {\n throw new Error(\n `Failed to import mnemonic wallet: wallet not found after creation`,\n );\n }\n if (!isMnemonicWalletObject(localWallet)) {\n throw new Error(\n `Failed to import mnemonic wallet: wallet is not of type 'mnemonic'`,\n );\n }\n return localWallet;\n}\n\n/**\n * Returns the local simple-keyring wallet, or `undefined` if none exists yet.\n *\n * The private-key wallet ID is static (derived solely from {@link KeyringTypes.simple}),\n * so no runtime argument is needed to locate it.\n *\n * @param context - Import context.\n * @returns The keyring wallet object, or `undefined` if not present in state.\n */\nfunction findLocalWalletPrivateKey(\n context: ImportContext,\n): AccountWalletKeyringObject | undefined {\n const localWalletId = toAccountWalletId(\n AccountWalletType.Keyring,\n KeyringTypes.simple,\n );\n const localWallet = context.getState().accountTree.wallets[localWalletId];\n return localWallet?.type === AccountWalletType.Keyring\n ? localWallet\n : undefined;\n}\n\n/**\n * Returns `true` if the given payload group is already present in the local simple-keyring wallet.\n *\n * @param localWallet - The local simple-keyring wallet, or `undefined` if none exists yet.\n * @param group - The payload group entry to check.\n * @returns Whether the group's account is already tracked locally.\n */\nfunction hasLocalGroupPrivateKey(\n localWallet: AccountWalletKeyringObject | undefined,\n group: AccountWalletPrivateKeyGroupEntry,\n): boolean {\n const address = parsePayloadGroupId(group.id).subId;\n\n const localWalletId = toAccountWalletId(\n AccountWalletType.Keyring,\n KeyringTypes.simple,\n );\n const localGroupId = toAccountGroupId(localWalletId, address);\n const localGroup = localWallet?.groups[localGroupId];\n\n const accountId = getUUIDFromAddressOfNormalAccount(address);\n\n return localGroup?.accounts.some((id) => id === accountId) ?? false;\n}\n\n/**\n * Applies name, pinned, and hidden metadata from a payload group entry to a local group.\n *\n * @param context - Import context providing the metadata setters.\n * @param localGroupId - Local group ID to update.\n * @param payloadGroupMetadata - Metadata from the payload group entry.\n */\nfunction setGroupMetadata(\n context: ImportContext,\n localGroupId: AccountGroupId,\n payloadGroupMetadata: AccountWalletMnemonicGroupEntry['metadata'],\n): void {\n if (payloadGroupMetadata) {\n context.setAccountGroupName(localGroupId, payloadGroupMetadata.name);\n context.setAccountGroupPinned(localGroupId, payloadGroupMetadata.pinned);\n context.setAccountGroupHidden(localGroupId, payloadGroupMetadata.hidden);\n }\n}\n\n/**\n * Computes the contiguous ranges of group indices that are present in the payload\n * but absent from the local wallet, so they can be created in batches.\n *\n * @param localWallet - The local entropy wallet to check existing groups against.\n * @param payloadGroups - Ordered group entries from the payload.\n * @returns An array of `[fromGroupIndex, toGroupIndex]` ranges to create.\n */\nfunction getRangesFromPayloadGroups(\n localWallet: AccountWalletEntropyObject,\n payloadGroups: AccountWalletMnemonicGroupEntry[],\n): [number, number][] {\n let rangeIndex: number | undefined;\n const ranges: [number, number][] = [];\n\n // Keep track of the last payload group so we can close the final range if needed.\n let lastPayloadGroup: AccountWalletMnemonicGroupEntry | undefined;\n for (const payloadGroup of payloadGroups) {\n const localGroupId = toMultichainAccountGroupId(\n localWallet.id,\n payloadGroup.groupIndex,\n );\n\n if (localWallet.groups[localGroupId]) {\n if (rangeIndex !== undefined) {\n ranges.push([rangeIndex, payloadGroup.groupIndex - 1]);\n rangeIndex = undefined;\n }\n continue;\n }\n\n rangeIndex ??= payloadGroup.groupIndex;\n lastPayloadGroup = payloadGroup;\n }\n\n if (rangeIndex !== undefined && lastPayloadGroup !== undefined) {\n ranges.push([rangeIndex, lastPayloadGroup.groupIndex]);\n }\n\n return ranges;\n}\n\n/**\n * Applies a mnemonic wallet payload entry to the local state.\n *\n * If no local wallet with the same entropy source ID exists and a mnemonic is\n * present in the payload, a new HD wallet is created via\n * `MultichainAccountService:createMultichainAccountWallet`. Missing groups are\n * created in batches via `MultichainAccountService:createMultichainAccountGroups`.\n * Metadata (name, pinned, hidden) is applied to all groups afterward.\n *\n * @param context - Import context.\n * @param payloadWallet - The mnemonic wallet entry from the payload.\n */\nasync function importMnemonicWallet(\n context: ImportContext,\n payloadWallet: AccountWalletMnemonicPayload,\n): Promise<void> {\n // Find the local wallet with the same entropy source ID if it exists.\n let localWallet = await findLocalWalletMnemonicFromPayloadId(\n context,\n payloadWallet.id,\n );\n\n if (!localWallet) {\n if (!payloadWallet.value) {\n // No mnemonic in payload and wallet doesn't exist locally -- nothing to do.\n return;\n }\n\n // Import the mnemonic as a new HD wallet.\n const mnemonic = decodeBytes(payloadWallet.value);\n const { id } = await context.messenger.call(\n 'MultichainAccountService:createMultichainAccountWallet',\n { type: 'import', mnemonic },\n );\n\n // Event handlers fire synchronously, so the wallet is in the tree now.\n localWallet = findLocalWalletMnemonicFromId(context, id);\n }\n\n if (payloadWallet.metadata) {\n context.setWalletName(localWallet.id, payloadWallet.metadata.name);\n }\n\n // Apply metadata to groups that are already present locally before attempting\n // to create missing ones. If createMultichainAccountGroups throws partway\n // through, these groups would otherwise be left without their payload metadata.\n const localExistingGroupIds = new Set(Object.keys(localWallet.groups));\n for (const payloadGroup of payloadWallet.groups) {\n const localGroupId = toMultichainAccountGroupId(\n localWallet.id,\n payloadGroup.groupIndex,\n );\n if (localExistingGroupIds.has(localGroupId)) {\n setGroupMetadata(context, localGroupId, payloadGroup.metadata);\n }\n }\n\n for (const range of getRangesFromPayloadGroups(\n localWallet,\n payloadWallet.groups,\n )) {\n await context.messenger.call(\n 'MultichainAccountService:createMultichainAccountGroups',\n {\n entropySource: localWallet.metadata.entropy.id,\n fromGroupIndex: range[0],\n toGroupIndex: range[1],\n },\n );\n }\n\n // Re-read wallet after groups creation.\n localWallet = findLocalWalletMnemonicFromId(context, localWallet.id);\n\n // Apply metadata to newly created groups. Skip groups that were already\n // handled above, and guard against groups that failed to be created so a\n // partial failure doesn't throw and abort the rest of the snapshot import.\n for (const payloadGroup of payloadWallet.groups) {\n const localGroupId = toMultichainAccountGroupId(\n localWallet.id,\n payloadGroup.groupIndex,\n );\n if (\n localExistingGroupIds.has(localGroupId) ||\n !localWallet.groups[localGroupId]\n ) {\n continue;\n }\n setGroupMetadata(context, localGroupId, payloadGroup.metadata);\n }\n}\n\n/**\n * Applies private-key wallet group entries from the payload to the local state.\n *\n * For each group the account address is derived from the payload group ID. If the\n * account does not yet exist locally and a private key is provided, it is imported\n * via `KeyringController:withKeyringV2` using the `'private-key:import'` constructor.\n * Metadata (name, pinned, hidden) is then applied to the local group.\n *\n * @param context - Import context.\n * @param payloadGroups - Private-key group entries from the payload.\n */\nasync function importPrivateKeyWallet(\n context: ImportContext,\n payloadGroups: AccountWalletPrivateKeyGroupEntry[],\n): Promise<void> {\n const localWalletId = toAccountWalletId(\n AccountWalletType.Keyring,\n KeyringTypes.simple,\n );\n\n // Only EVM EOA accounts are supported for now. Non-EVM private keys require\n // Snap-based import routing (ADR-0007), which is not yet implemented. Skip the\n // entire entry so payloads from future clients are accepted without crashing.\n // In the same pass, collect groups whose key is not yet present locally.\n const localWallet = findLocalWalletPrivateKey(context);\n const supportedGroups: AccountWalletPrivateKeyGroupEntry[] = [];\n const missingGroups: AccountWalletPrivateKeyGroupEntry[] = [];\n\n for (const group of payloadGroups) {\n const type = group.value?.type;\n if (type !== undefined && type !== EthAccountType.Eoa) {\n continue;\n }\n\n supportedGroups.push(group);\n\n if (\n !hasLocalGroupPrivateKey(localWallet, group) &&\n group.value !== undefined\n ) {\n missingGroups.push(group);\n }\n }\n\n // Import all missing keys in a single :withController call to avoid\n // triggering multiple `KeyringController:stateChanged` events.\n if (missingGroups.length > 0) {\n await context.messenger.call(\n 'KeyringController:withController',\n async (controller) => {\n // Find the existing simple keyring or create one if this is the\n // first private-key import (e.g. during onboarding).\n const existing = controller.keyrings.find(\n ({ keyring }) => keyring.type === KeyringTypes.simple,\n );\n const { keyringV2 } =\n existing ?? (await controller.addNewKeyring(KeyringTypes.simple));\n\n if (!keyringV2) {\n throw new Error('Simple keyring has no v2 interface');\n }\n\n for (const group of missingGroups) {\n // Safe to assert `group.value` is present because we filtered out groups\n // without a `value` above.\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const { privateKey: privateKeyBytes, encoding } = group.value!;\n const privateKey = new TextDecoder().decode(\n decodeBytes(privateKeyBytes),\n );\n const [account] = await keyringV2.createAccounts({\n type: 'private-key:import',\n accountType: EthAccountType.Eoa,\n privateKey,\n encoding,\n });\n\n if (!account) {\n throw new Error('Failed to import private key for account');\n }\n }\n },\n );\n }\n\n // Re-read state once after all imports, then apply metadata to every group\n // that now exists locally.\n for (const group of supportedGroups) {\n const address = parsePayloadGroupId(group.id).subId;\n const localGroupId = toAccountGroupId(localWalletId, address);\n const localGroup = findLocalWalletPrivateKey(context)?.groups[localGroupId];\n if (!localGroup) {\n continue;\n }\n setGroupMetadata(context, localGroup.id, group.metadata);\n }\n}\n\n/**\n * Applies an {@link AccountTreeSnapshot} to the current controller state.\n *\n * The snapshot must already have been validated — typically via\n * {@link AccountTreeSnapshot.deserialize}. For each retained wallet:\n *\n * - `'mnemonic'`: imports the mnemonic when provided and not already present,\n * then applies metadata to all groups.\n * - `'private-key'`: imports each retained group's key when provided and not\n * already present, then applies metadata.\n *\n * @param context - Import context providing state, messenger, and setters.\n * @param snapshot - The validated snapshot to import.\n */\nexport async function importState(\n context: ImportContext,\n snapshot: AccountTreeSnapshot,\n): Promise<void> {\n const payload = snapshot.serialize();\n\n for (const wallet of payload.wallets) {\n if (wallet.type === AccountWalletPayloadType.Mnemonic) {\n await importMnemonicWallet(context, wallet);\n } else {\n await importPrivateKeyWallet(context, wallet.groups);\n }\n }\n}\n"]}
1
+ {"version":3,"file":"import.cjs","sourceRoot":"","sources":["../../src/state/import.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n AccountWalletType,\n toAccountGroupId,\n toAccountWalletId,\n toMultichainAccountGroupId,\n} from '@metamask/account-api';\nimport type { AccountGroupId, AccountWalletId } from '@metamask/account-api';\nimport { getUUIDFromAddressOfNormalAccount } from '@metamask/accounts-controller';\nimport { HdKeyring } from '@metamask/eth-hd-keyring/v2';\nimport { EthAccountType } from '@metamask/keyring-api';\nimport { KeyringTypes } from '@metamask/keyring-controller';\n\nimport type {\n AccountTreeControllerMessenger,\n AccountTreeControllerState,\n} from '../types.js';\nimport type {\n AccountWalletEntropyObject,\n AccountWalletKeyringObject,\n} from '../wallet.js';\nimport { isMnemonicWalletObject } from './export.js';\nimport type {\n AccountWalletMnemonicGroupEntry,\n AccountWalletMnemonicPayload,\n AccountWalletPayloadId,\n AccountWalletPrivateKeyGroupEntry,\n} from './payload.js';\nimport {\n AccountWalletPayloadType,\n parsePayloadGroupId,\n toWalletPayloadId,\n} from './payload.js';\nimport type { AccountTreeSnapshot } from './snapshot.js';\nimport { decodeBytes } from './utils.js';\n\n/** Context required by {@link importState}. */\nexport type ImportContext = {\n getState: () => AccountTreeControllerState;\n messenger: AccountTreeControllerMessenger;\n setWalletName: (walletId: AccountWalletId, name: string) => void;\n /** Sets a group name. Implementations must resolve name conflicts automatically. */\n setAccountGroupName: (groupId: AccountGroupId, name: string) => void;\n setAccountGroupPinned: (groupId: AccountGroupId, pinned: boolean) => void;\n setAccountGroupHidden: (groupId: AccountGroupId, hidden: boolean) => void;\n};\n\n/**\n * Searches the local wallet tree for an entropy wallet whose stable payload ID\n * matches `payloadWalletId`. The entropy source ID is derived on-the-fly via\n * `KeyringController:withKeyringV2Unsafe` rather than relying on cached metadata.\n *\n * @param context - Import context.\n * @param payloadWalletId - Payload wallet ID to match against.\n * @returns The matching local entropy wallet, or `undefined` if not found.\n */\nasync function findLocalWalletMnemonicFromPayloadId(\n context: ImportContext,\n payloadWalletId: AccountWalletPayloadId,\n): Promise<AccountWalletEntropyObject | undefined> {\n const wallets = Object.values(context.getState().accountTree.wallets);\n\n for (const wallet of wallets) {\n if (isMnemonicWalletObject(wallet)) {\n const result = await context.messenger.call(\n 'KeyringController:withKeyringV2Unsafe',\n { id: wallet.metadata.entropy.id },\n async ({ keyring }) => {\n const hdKeyring = keyring as HdKeyring;\n\n return toWalletPayloadId(await hdKeyring.toEntropySourceId());\n },\n );\n\n const localPayloadId = result as AccountWalletPayloadId;\n if (localPayloadId === payloadWalletId) {\n return wallet;\n }\n }\n }\n\n return undefined;\n}\n\n/**\n * Returns the local entropy wallet for the given ID.\n *\n * @param context - Import context.\n * @param id - Local wallet ID.\n * @returns The entropy wallet object.\n * @throws If the wallet is not found or is not an entropy wallet.\n */\nfunction findLocalWalletMnemonicFromId(\n context: ImportContext,\n id: AccountWalletId,\n): AccountWalletEntropyObject {\n const localWallets = context.getState().accountTree.wallets;\n const localWallet = localWallets[id];\n\n if (!localWallet) {\n throw new Error(\n `Failed to import mnemonic wallet: wallet not found after creation`,\n );\n }\n if (!isMnemonicWalletObject(localWallet)) {\n throw new Error(\n `Failed to import mnemonic wallet: wallet is not of type 'mnemonic'`,\n );\n }\n return localWallet;\n}\n\n/**\n * Returns the local simple-keyring wallet, or `undefined` if none exists yet.\n *\n * The private-key wallet ID is static (derived solely from {@link KeyringTypes.simple}),\n * so no runtime argument is needed to locate it.\n *\n * @param context - Import context.\n * @returns The keyring wallet object, or `undefined` if not present in state.\n */\nfunction findLocalWalletPrivateKey(\n context: ImportContext,\n): AccountWalletKeyringObject | undefined {\n const localWalletId = toAccountWalletId(\n AccountWalletType.Keyring,\n KeyringTypes.simple,\n );\n const localWallet = context.getState().accountTree.wallets[localWalletId];\n return localWallet?.type === AccountWalletType.Keyring\n ? localWallet\n : undefined;\n}\n\n/**\n * Returns `true` if the given payload group is already present in the local simple-keyring wallet.\n *\n * @param localWallet - The local simple-keyring wallet, or `undefined` if none exists yet.\n * @param group - The payload group entry to check.\n * @returns Whether the group's account is already tracked locally.\n */\nfunction hasLocalGroupPrivateKey(\n localWallet: AccountWalletKeyringObject | undefined,\n group: AccountWalletPrivateKeyGroupEntry,\n): boolean {\n const address = parsePayloadGroupId(group.id).subId;\n\n const localWalletId = toAccountWalletId(\n AccountWalletType.Keyring,\n KeyringTypes.simple,\n );\n const localGroupId = toAccountGroupId(localWalletId, address);\n const localGroup = localWallet?.groups[localGroupId];\n\n const accountId = getUUIDFromAddressOfNormalAccount(address);\n\n return localGroup?.accounts.some((id) => id === accountId) ?? false;\n}\n\n/**\n * Applies name, pinned, and hidden metadata from a payload group entry to a local group.\n *\n * @param context - Import context providing the metadata setters.\n * @param localGroupId - Local group ID to update.\n * @param payloadGroupMetadata - Metadata from the payload group entry.\n */\nfunction setGroupMetadata(\n context: ImportContext,\n localGroupId: AccountGroupId,\n payloadGroupMetadata: AccountWalletMnemonicGroupEntry['metadata'],\n): void {\n context.setAccountGroupName(localGroupId, payloadGroupMetadata.name);\n context.setAccountGroupPinned(localGroupId, payloadGroupMetadata.pinned);\n context.setAccountGroupHidden(localGroupId, payloadGroupMetadata.hidden);\n}\n\n/**\n * Computes the contiguous ranges of group indices that are present in the payload\n * but absent from the local wallet, so they can be created in batches.\n *\n * @param localWallet - The local entropy wallet to check existing groups against.\n * @param payloadGroups - Ordered group entries from the payload.\n * @returns An array of `[fromGroupIndex, toGroupIndex]` ranges to create.\n */\nfunction getRangesFromPayloadGroups(\n localWallet: AccountWalletEntropyObject,\n payloadGroups: AccountWalletMnemonicGroupEntry[],\n): [number, number][] {\n let rangeIndex: number | undefined;\n const ranges: [number, number][] = [];\n\n // Keep track of the last payload group so we can close the final range if needed.\n let lastPayloadGroup: AccountWalletMnemonicGroupEntry | undefined;\n for (const payloadGroup of payloadGroups) {\n const localGroupId = toMultichainAccountGroupId(\n localWallet.id,\n payloadGroup.groupIndex,\n );\n\n if (localWallet.groups[localGroupId]) {\n if (rangeIndex !== undefined) {\n ranges.push([rangeIndex, payloadGroup.groupIndex - 1]);\n rangeIndex = undefined;\n }\n continue;\n }\n\n rangeIndex ??= payloadGroup.groupIndex;\n lastPayloadGroup = payloadGroup;\n }\n\n if (rangeIndex !== undefined && lastPayloadGroup !== undefined) {\n ranges.push([rangeIndex, lastPayloadGroup.groupIndex]);\n }\n\n return ranges;\n}\n\n/**\n * Applies a mnemonic wallet payload entry to the local state.\n *\n * If no local wallet with the same entropy source ID exists and a mnemonic is\n * present in the payload, a new HD wallet is created via\n * `MultichainAccountService:createMultichainAccountWallet`. Missing groups are\n * created in batches via `MultichainAccountService:createMultichainAccountGroups`.\n * Metadata (name, pinned, hidden) is applied to all groups afterward.\n *\n * @param context - Import context.\n * @param payloadWallet - The mnemonic wallet entry from the payload.\n */\nasync function importMnemonicWallet(\n context: ImportContext,\n payloadWallet: AccountWalletMnemonicPayload,\n): Promise<void> {\n // Find the local wallet with the same entropy source ID if it exists.\n let localWallet = await findLocalWalletMnemonicFromPayloadId(\n context,\n payloadWallet.id,\n );\n\n if (!localWallet) {\n if (!payloadWallet.value) {\n // No mnemonic in payload and wallet doesn't exist locally -- nothing to do.\n return;\n }\n\n // Import the mnemonic as a new HD wallet.\n const mnemonic = decodeBytes(payloadWallet.value);\n const { id } = await context.messenger.call(\n 'MultichainAccountService:createMultichainAccountWallet',\n { type: 'import', mnemonic },\n );\n\n // Event handlers fire synchronously, so the wallet is in the tree now.\n localWallet = findLocalWalletMnemonicFromId(context, id);\n }\n\n context.setWalletName(localWallet.id, payloadWallet.metadata.name);\n\n // Apply metadata to groups that are already present locally before attempting\n // to create missing ones. If createMultichainAccountGroups throws partway\n // through, these groups would otherwise be left without their payload metadata.\n const localExistingGroupIds = new Set(Object.keys(localWallet.groups));\n for (const payloadGroup of payloadWallet.groups) {\n const localGroupId = toMultichainAccountGroupId(\n localWallet.id,\n payloadGroup.groupIndex,\n );\n if (localExistingGroupIds.has(localGroupId)) {\n setGroupMetadata(context, localGroupId, payloadGroup.metadata);\n }\n }\n\n for (const range of getRangesFromPayloadGroups(\n localWallet,\n payloadWallet.groups,\n )) {\n await context.messenger.call(\n 'MultichainAccountService:createMultichainAccountGroups',\n {\n entropySource: localWallet.metadata.entropy.id,\n fromGroupIndex: range[0],\n toGroupIndex: range[1],\n },\n );\n }\n\n // Re-read wallet after groups creation.\n localWallet = findLocalWalletMnemonicFromId(context, localWallet.id);\n\n // Apply metadata to newly created groups. Skip groups that were already\n // handled above, and guard against groups that failed to be created so a\n // partial failure doesn't throw and abort the rest of the snapshot import.\n for (const payloadGroup of payloadWallet.groups) {\n const localGroupId = toMultichainAccountGroupId(\n localWallet.id,\n payloadGroup.groupIndex,\n );\n if (\n localExistingGroupIds.has(localGroupId) ||\n !localWallet.groups[localGroupId]\n ) {\n continue;\n }\n setGroupMetadata(context, localGroupId, payloadGroup.metadata);\n }\n}\n\n/**\n * Applies private-key wallet group entries from the payload to the local state.\n *\n * For each group the account address is derived from the payload group ID. If the\n * account does not yet exist locally and a private key is provided, it is imported\n * via `KeyringController:withKeyringV2` using the `'private-key:import'` constructor.\n * Metadata (name, pinned, hidden) is then applied to the local group.\n *\n * @param context - Import context.\n * @param payloadGroups - Private-key group entries from the payload.\n */\nasync function importPrivateKeyWallet(\n context: ImportContext,\n payloadGroups: AccountWalletPrivateKeyGroupEntry[],\n): Promise<void> {\n const localWalletId = toAccountWalletId(\n AccountWalletType.Keyring,\n KeyringTypes.simple,\n );\n\n // Only EVM EOA accounts are supported for now. Non-EVM private keys require\n // Snap-based import routing (ADR-0007), which is not yet implemented. Skip the\n // entire entry so payloads from future clients are accepted without crashing.\n // In the same pass, collect groups whose key is not yet present locally.\n const localWallet = findLocalWalletPrivateKey(context);\n const supportedGroups: AccountWalletPrivateKeyGroupEntry[] = [];\n const missingGroups: AccountWalletPrivateKeyGroupEntry[] = [];\n\n for (const group of payloadGroups) {\n const type = group.value?.type;\n if (type !== undefined && type !== EthAccountType.Eoa) {\n continue;\n }\n\n supportedGroups.push(group);\n\n if (\n !hasLocalGroupPrivateKey(localWallet, group) &&\n group.value !== undefined\n ) {\n missingGroups.push(group);\n }\n }\n\n // Import all missing keys in a single :withController call to avoid\n // triggering multiple `KeyringController:stateChanged` events.\n if (missingGroups.length > 0) {\n await context.messenger.call(\n 'KeyringController:withController',\n async (controller) => {\n // Find the existing simple keyring or create one if this is the\n // first private-key import (e.g. during onboarding).\n const existing = controller.keyrings.find(\n ({ keyring }) => keyring.type === KeyringTypes.simple,\n );\n const { keyringV2 } =\n existing ?? (await controller.addNewKeyring(KeyringTypes.simple));\n\n if (!keyringV2) {\n throw new Error('Simple keyring has no v2 interface');\n }\n\n for (const group of missingGroups) {\n // Safe to assert `group.value` is present because we filtered out groups\n // without a `value` above.\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const { privateKey: privateKeyBytes, encoding } = group.value!;\n const privateKey = new TextDecoder().decode(\n decodeBytes(privateKeyBytes),\n );\n const [account] = await keyringV2.createAccounts({\n type: 'private-key:import',\n accountType: EthAccountType.Eoa,\n privateKey,\n encoding,\n });\n\n if (!account) {\n throw new Error('Failed to import private key for account');\n }\n }\n },\n );\n }\n\n // Re-read state once after all imports, then apply metadata to every group\n // that now exists locally.\n for (const group of supportedGroups) {\n const address = parsePayloadGroupId(group.id).subId;\n const localGroupId = toAccountGroupId(localWalletId, address);\n const localGroup = findLocalWalletPrivateKey(context)?.groups[localGroupId];\n if (!localGroup) {\n continue;\n }\n setGroupMetadata(context, localGroup.id, group.metadata);\n }\n}\n\n/**\n * Applies an {@link AccountTreeSnapshot} to the current controller state.\n *\n * The snapshot must already have been validated — typically via\n * {@link AccountTreeSnapshot.deserialize}. For each retained wallet:\n *\n * - `'mnemonic'`: imports the mnemonic when provided and not already present,\n * then applies metadata to all groups.\n * - `'private-key'`: imports each retained group's key when provided and not\n * already present, then applies metadata.\n *\n * @param context - Import context providing state, messenger, and setters.\n * @param snapshot - The validated snapshot to import.\n */\nexport async function importState(\n context: ImportContext,\n snapshot: AccountTreeSnapshot,\n): Promise<void> {\n const payload = snapshot.serialize();\n\n for (const wallet of payload.wallets) {\n if (wallet.type === AccountWalletPayloadType.Mnemonic) {\n await importMnemonicWallet(context, wallet);\n } else {\n await importPrivateKeyWallet(context, wallet.groups);\n }\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"import.d.cts","sourceRoot":"","sources":["../../src/state/import.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,8BAA8B;AAM7E,OAAO,KAAK,EACV,8BAA8B,EAC9B,0BAA0B,EAC3B,qBAAoB;AAiBrB,OAAO,KAAK,EAAE,mBAAmB,EAAE,uBAAsB;AAGzD,+CAA+C;AAC/C,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,MAAM,0BAA0B,CAAC;IAC3C,SAAS,EAAE,8BAA8B,CAAC;IAC1C,aAAa,EAAE,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjE,oFAAoF;IACpF,mBAAmB,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,qBAAqB,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1E,qBAAqB,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CAC3E,CAAC;AA6WF;;;;;;;;;;;;;GAaG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,mBAAmB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAUf"}
1
+ {"version":3,"file":"import.d.cts","sourceRoot":"","sources":["../../src/state/import.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,8BAA8B;AAM7E,OAAO,KAAK,EACV,8BAA8B,EAC9B,0BAA0B,EAC3B,qBAAoB;AAiBrB,OAAO,KAAK,EAAE,mBAAmB,EAAE,uBAAsB;AAGzD,+CAA+C;AAC/C,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,MAAM,0BAA0B,CAAC;IAC3C,SAAS,EAAE,8BAA8B,CAAC;IAC1C,aAAa,EAAE,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjE,oFAAoF;IACpF,mBAAmB,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,qBAAqB,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1E,qBAAqB,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CAC3E,CAAC;AAyWF;;;;;;;;;;;;;GAaG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,mBAAmB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAUf"}
@@ -1 +1 @@
1
- {"version":3,"file":"import.d.mts","sourceRoot":"","sources":["../../src/state/import.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,8BAA8B;AAM7E,OAAO,KAAK,EACV,8BAA8B,EAC9B,0BAA0B,EAC3B,qBAAoB;AAiBrB,OAAO,KAAK,EAAE,mBAAmB,EAAE,uBAAsB;AAGzD,+CAA+C;AAC/C,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,MAAM,0BAA0B,CAAC;IAC3C,SAAS,EAAE,8BAA8B,CAAC;IAC1C,aAAa,EAAE,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjE,oFAAoF;IACpF,mBAAmB,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,qBAAqB,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1E,qBAAqB,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CAC3E,CAAC;AA6WF;;;;;;;;;;;;;GAaG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,mBAAmB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAUf"}
1
+ {"version":3,"file":"import.d.mts","sourceRoot":"","sources":["../../src/state/import.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,8BAA8B;AAM7E,OAAO,KAAK,EACV,8BAA8B,EAC9B,0BAA0B,EAC3B,qBAAoB;AAiBrB,OAAO,KAAK,EAAE,mBAAmB,EAAE,uBAAsB;AAGzD,+CAA+C;AAC/C,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,MAAM,0BAA0B,CAAC;IAC3C,SAAS,EAAE,8BAA8B,CAAC;IAC1C,aAAa,EAAE,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjE,oFAAoF;IACpF,mBAAmB,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,qBAAqB,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1E,qBAAqB,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CAC3E,CAAC;AAyWF;;;;;;;;;;;;;GAaG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,mBAAmB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAUf"}
@@ -89,11 +89,9 @@ function hasLocalGroupPrivateKey(localWallet, group) {
89
89
  * @param payloadGroupMetadata - Metadata from the payload group entry.
90
90
  */
91
91
  function setGroupMetadata(context, localGroupId, payloadGroupMetadata) {
92
- if (payloadGroupMetadata) {
93
- context.setAccountGroupName(localGroupId, payloadGroupMetadata.name);
94
- context.setAccountGroupPinned(localGroupId, payloadGroupMetadata.pinned);
95
- context.setAccountGroupHidden(localGroupId, payloadGroupMetadata.hidden);
96
- }
92
+ context.setAccountGroupName(localGroupId, payloadGroupMetadata.name);
93
+ context.setAccountGroupPinned(localGroupId, payloadGroupMetadata.pinned);
94
+ context.setAccountGroupHidden(localGroupId, payloadGroupMetadata.hidden);
97
95
  }
98
96
  /**
99
97
  * Computes the contiguous ranges of group indices that are present in the payload
@@ -151,9 +149,7 @@ async function importMnemonicWallet(context, payloadWallet) {
151
149
  // Event handlers fire synchronously, so the wallet is in the tree now.
152
150
  localWallet = findLocalWalletMnemonicFromId(context, id);
153
151
  }
154
- if (payloadWallet.metadata) {
155
- context.setWalletName(localWallet.id, payloadWallet.metadata.name);
156
- }
152
+ context.setWalletName(localWallet.id, payloadWallet.metadata.name);
157
153
  // Apply metadata to groups that are already present locally before attempting
158
154
  // to create missing ones. If createMultichainAccountGroups throws partway
159
155
  // through, these groups would otherwise be left without their payload metadata.
@@ -1 +1 @@
1
- {"version":3,"file":"import.mjs","sourceRoot":"","sources":["../../src/state/import.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n AccountWalletType,\n toAccountGroupId,\n toAccountWalletId,\n toMultichainAccountGroupId,\n} from '@metamask/account-api';\nimport type { AccountGroupId, AccountWalletId } from '@metamask/account-api';\nimport { getUUIDFromAddressOfNormalAccount } from '@metamask/accounts-controller';\nimport { HdKeyring } from '@metamask/eth-hd-keyring/v2';\nimport { EthAccountType } from '@metamask/keyring-api';\nimport { KeyringTypes } from '@metamask/keyring-controller';\n\nimport type {\n AccountTreeControllerMessenger,\n AccountTreeControllerState,\n} from '../types.js';\nimport type {\n AccountWalletEntropyObject,\n AccountWalletKeyringObject,\n} from '../wallet.js';\nimport { isMnemonicWalletObject } from './export.js';\nimport type {\n AccountWalletMnemonicGroupEntry,\n AccountWalletMnemonicPayload,\n AccountWalletPayloadId,\n AccountWalletPrivateKeyGroupEntry,\n} from './payload.js';\nimport {\n AccountWalletPayloadType,\n parsePayloadGroupId,\n toWalletPayloadId,\n} from './payload.js';\nimport type { AccountTreeSnapshot } from './snapshot.js';\nimport { decodeBytes } from './utils.js';\n\n/** Context required by {@link importState}. */\nexport type ImportContext = {\n getState: () => AccountTreeControllerState;\n messenger: AccountTreeControllerMessenger;\n setWalletName: (walletId: AccountWalletId, name: string) => void;\n /** Sets a group name. Implementations must resolve name conflicts automatically. */\n setAccountGroupName: (groupId: AccountGroupId, name: string) => void;\n setAccountGroupPinned: (groupId: AccountGroupId, pinned: boolean) => void;\n setAccountGroupHidden: (groupId: AccountGroupId, hidden: boolean) => void;\n};\n\n/**\n * Searches the local wallet tree for an entropy wallet whose stable payload ID\n * matches `payloadWalletId`. The entropy source ID is derived on-the-fly via\n * `KeyringController:withKeyringV2Unsafe` rather than relying on cached metadata.\n *\n * @param context - Import context.\n * @param payloadWalletId - Payload wallet ID to match against.\n * @returns The matching local entropy wallet, or `undefined` if not found.\n */\nasync function findLocalWalletMnemonicFromPayloadId(\n context: ImportContext,\n payloadWalletId: AccountWalletPayloadId,\n): Promise<AccountWalletEntropyObject | undefined> {\n const wallets = Object.values(context.getState().accountTree.wallets);\n\n for (const wallet of wallets) {\n if (isMnemonicWalletObject(wallet)) {\n const result = await context.messenger.call(\n 'KeyringController:withKeyringV2Unsafe',\n { id: wallet.metadata.entropy.id },\n async ({ keyring }) => {\n const hdKeyring = keyring as HdKeyring;\n\n return toWalletPayloadId(await hdKeyring.toEntropySourceId());\n },\n );\n\n const localPayloadId = result as AccountWalletPayloadId;\n if (localPayloadId === payloadWalletId) {\n return wallet;\n }\n }\n }\n\n return undefined;\n}\n\n/**\n * Returns the local entropy wallet for the given ID.\n *\n * @param context - Import context.\n * @param id - Local wallet ID.\n * @returns The entropy wallet object.\n * @throws If the wallet is not found or is not an entropy wallet.\n */\nfunction findLocalWalletMnemonicFromId(\n context: ImportContext,\n id: AccountWalletId,\n): AccountWalletEntropyObject {\n const localWallets = context.getState().accountTree.wallets;\n const localWallet = localWallets[id];\n\n if (!localWallet) {\n throw new Error(\n `Failed to import mnemonic wallet: wallet not found after creation`,\n );\n }\n if (!isMnemonicWalletObject(localWallet)) {\n throw new Error(\n `Failed to import mnemonic wallet: wallet is not of type 'mnemonic'`,\n );\n }\n return localWallet;\n}\n\n/**\n * Returns the local simple-keyring wallet, or `undefined` if none exists yet.\n *\n * The private-key wallet ID is static (derived solely from {@link KeyringTypes.simple}),\n * so no runtime argument is needed to locate it.\n *\n * @param context - Import context.\n * @returns The keyring wallet object, or `undefined` if not present in state.\n */\nfunction findLocalWalletPrivateKey(\n context: ImportContext,\n): AccountWalletKeyringObject | undefined {\n const localWalletId = toAccountWalletId(\n AccountWalletType.Keyring,\n KeyringTypes.simple,\n );\n const localWallet = context.getState().accountTree.wallets[localWalletId];\n return localWallet?.type === AccountWalletType.Keyring\n ? localWallet\n : undefined;\n}\n\n/**\n * Returns `true` if the given payload group is already present in the local simple-keyring wallet.\n *\n * @param localWallet - The local simple-keyring wallet, or `undefined` if none exists yet.\n * @param group - The payload group entry to check.\n * @returns Whether the group's account is already tracked locally.\n */\nfunction hasLocalGroupPrivateKey(\n localWallet: AccountWalletKeyringObject | undefined,\n group: AccountWalletPrivateKeyGroupEntry,\n): boolean {\n const address = parsePayloadGroupId(group.id).subId;\n\n const localWalletId = toAccountWalletId(\n AccountWalletType.Keyring,\n KeyringTypes.simple,\n );\n const localGroupId = toAccountGroupId(localWalletId, address);\n const localGroup = localWallet?.groups[localGroupId];\n\n const accountId = getUUIDFromAddressOfNormalAccount(address);\n\n return localGroup?.accounts.some((id) => id === accountId) ?? false;\n}\n\n/**\n * Applies name, pinned, and hidden metadata from a payload group entry to a local group.\n *\n * @param context - Import context providing the metadata setters.\n * @param localGroupId - Local group ID to update.\n * @param payloadGroupMetadata - Metadata from the payload group entry.\n */\nfunction setGroupMetadata(\n context: ImportContext,\n localGroupId: AccountGroupId,\n payloadGroupMetadata: AccountWalletMnemonicGroupEntry['metadata'],\n): void {\n if (payloadGroupMetadata) {\n context.setAccountGroupName(localGroupId, payloadGroupMetadata.name);\n context.setAccountGroupPinned(localGroupId, payloadGroupMetadata.pinned);\n context.setAccountGroupHidden(localGroupId, payloadGroupMetadata.hidden);\n }\n}\n\n/**\n * Computes the contiguous ranges of group indices that are present in the payload\n * but absent from the local wallet, so they can be created in batches.\n *\n * @param localWallet - The local entropy wallet to check existing groups against.\n * @param payloadGroups - Ordered group entries from the payload.\n * @returns An array of `[fromGroupIndex, toGroupIndex]` ranges to create.\n */\nfunction getRangesFromPayloadGroups(\n localWallet: AccountWalletEntropyObject,\n payloadGroups: AccountWalletMnemonicGroupEntry[],\n): [number, number][] {\n let rangeIndex: number | undefined;\n const ranges: [number, number][] = [];\n\n // Keep track of the last payload group so we can close the final range if needed.\n let lastPayloadGroup: AccountWalletMnemonicGroupEntry | undefined;\n for (const payloadGroup of payloadGroups) {\n const localGroupId = toMultichainAccountGroupId(\n localWallet.id,\n payloadGroup.groupIndex,\n );\n\n if (localWallet.groups[localGroupId]) {\n if (rangeIndex !== undefined) {\n ranges.push([rangeIndex, payloadGroup.groupIndex - 1]);\n rangeIndex = undefined;\n }\n continue;\n }\n\n rangeIndex ??= payloadGroup.groupIndex;\n lastPayloadGroup = payloadGroup;\n }\n\n if (rangeIndex !== undefined && lastPayloadGroup !== undefined) {\n ranges.push([rangeIndex, lastPayloadGroup.groupIndex]);\n }\n\n return ranges;\n}\n\n/**\n * Applies a mnemonic wallet payload entry to the local state.\n *\n * If no local wallet with the same entropy source ID exists and a mnemonic is\n * present in the payload, a new HD wallet is created via\n * `MultichainAccountService:createMultichainAccountWallet`. Missing groups are\n * created in batches via `MultichainAccountService:createMultichainAccountGroups`.\n * Metadata (name, pinned, hidden) is applied to all groups afterward.\n *\n * @param context - Import context.\n * @param payloadWallet - The mnemonic wallet entry from the payload.\n */\nasync function importMnemonicWallet(\n context: ImportContext,\n payloadWallet: AccountWalletMnemonicPayload,\n): Promise<void> {\n // Find the local wallet with the same entropy source ID if it exists.\n let localWallet = await findLocalWalletMnemonicFromPayloadId(\n context,\n payloadWallet.id,\n );\n\n if (!localWallet) {\n if (!payloadWallet.value) {\n // No mnemonic in payload and wallet doesn't exist locally -- nothing to do.\n return;\n }\n\n // Import the mnemonic as a new HD wallet.\n const mnemonic = decodeBytes(payloadWallet.value);\n const { id } = await context.messenger.call(\n 'MultichainAccountService:createMultichainAccountWallet',\n { type: 'import', mnemonic },\n );\n\n // Event handlers fire synchronously, so the wallet is in the tree now.\n localWallet = findLocalWalletMnemonicFromId(context, id);\n }\n\n if (payloadWallet.metadata) {\n context.setWalletName(localWallet.id, payloadWallet.metadata.name);\n }\n\n // Apply metadata to groups that are already present locally before attempting\n // to create missing ones. If createMultichainAccountGroups throws partway\n // through, these groups would otherwise be left without their payload metadata.\n const localExistingGroupIds = new Set(Object.keys(localWallet.groups));\n for (const payloadGroup of payloadWallet.groups) {\n const localGroupId = toMultichainAccountGroupId(\n localWallet.id,\n payloadGroup.groupIndex,\n );\n if (localExistingGroupIds.has(localGroupId)) {\n setGroupMetadata(context, localGroupId, payloadGroup.metadata);\n }\n }\n\n for (const range of getRangesFromPayloadGroups(\n localWallet,\n payloadWallet.groups,\n )) {\n await context.messenger.call(\n 'MultichainAccountService:createMultichainAccountGroups',\n {\n entropySource: localWallet.metadata.entropy.id,\n fromGroupIndex: range[0],\n toGroupIndex: range[1],\n },\n );\n }\n\n // Re-read wallet after groups creation.\n localWallet = findLocalWalletMnemonicFromId(context, localWallet.id);\n\n // Apply metadata to newly created groups. Skip groups that were already\n // handled above, and guard against groups that failed to be created so a\n // partial failure doesn't throw and abort the rest of the snapshot import.\n for (const payloadGroup of payloadWallet.groups) {\n const localGroupId = toMultichainAccountGroupId(\n localWallet.id,\n payloadGroup.groupIndex,\n );\n if (\n localExistingGroupIds.has(localGroupId) ||\n !localWallet.groups[localGroupId]\n ) {\n continue;\n }\n setGroupMetadata(context, localGroupId, payloadGroup.metadata);\n }\n}\n\n/**\n * Applies private-key wallet group entries from the payload to the local state.\n *\n * For each group the account address is derived from the payload group ID. If the\n * account does not yet exist locally and a private key is provided, it is imported\n * via `KeyringController:withKeyringV2` using the `'private-key:import'` constructor.\n * Metadata (name, pinned, hidden) is then applied to the local group.\n *\n * @param context - Import context.\n * @param payloadGroups - Private-key group entries from the payload.\n */\nasync function importPrivateKeyWallet(\n context: ImportContext,\n payloadGroups: AccountWalletPrivateKeyGroupEntry[],\n): Promise<void> {\n const localWalletId = toAccountWalletId(\n AccountWalletType.Keyring,\n KeyringTypes.simple,\n );\n\n // Only EVM EOA accounts are supported for now. Non-EVM private keys require\n // Snap-based import routing (ADR-0007), which is not yet implemented. Skip the\n // entire entry so payloads from future clients are accepted without crashing.\n // In the same pass, collect groups whose key is not yet present locally.\n const localWallet = findLocalWalletPrivateKey(context);\n const supportedGroups: AccountWalletPrivateKeyGroupEntry[] = [];\n const missingGroups: AccountWalletPrivateKeyGroupEntry[] = [];\n\n for (const group of payloadGroups) {\n const type = group.value?.type;\n if (type !== undefined && type !== EthAccountType.Eoa) {\n continue;\n }\n\n supportedGroups.push(group);\n\n if (\n !hasLocalGroupPrivateKey(localWallet, group) &&\n group.value !== undefined\n ) {\n missingGroups.push(group);\n }\n }\n\n // Import all missing keys in a single :withController call to avoid\n // triggering multiple `KeyringController:stateChanged` events.\n if (missingGroups.length > 0) {\n await context.messenger.call(\n 'KeyringController:withController',\n async (controller) => {\n // Find the existing simple keyring or create one if this is the\n // first private-key import (e.g. during onboarding).\n const existing = controller.keyrings.find(\n ({ keyring }) => keyring.type === KeyringTypes.simple,\n );\n const { keyringV2 } =\n existing ?? (await controller.addNewKeyring(KeyringTypes.simple));\n\n if (!keyringV2) {\n throw new Error('Simple keyring has no v2 interface');\n }\n\n for (const group of missingGroups) {\n // Safe to assert `group.value` is present because we filtered out groups\n // without a `value` above.\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const { privateKey: privateKeyBytes, encoding } = group.value!;\n const privateKey = new TextDecoder().decode(\n decodeBytes(privateKeyBytes),\n );\n const [account] = await keyringV2.createAccounts({\n type: 'private-key:import',\n accountType: EthAccountType.Eoa,\n privateKey,\n encoding,\n });\n\n if (!account) {\n throw new Error('Failed to import private key for account');\n }\n }\n },\n );\n }\n\n // Re-read state once after all imports, then apply metadata to every group\n // that now exists locally.\n for (const group of supportedGroups) {\n const address = parsePayloadGroupId(group.id).subId;\n const localGroupId = toAccountGroupId(localWalletId, address);\n const localGroup = findLocalWalletPrivateKey(context)?.groups[localGroupId];\n if (!localGroup) {\n continue;\n }\n setGroupMetadata(context, localGroup.id, group.metadata);\n }\n}\n\n/**\n * Applies an {@link AccountTreeSnapshot} to the current controller state.\n *\n * The snapshot must already have been validated — typically via\n * {@link AccountTreeSnapshot.deserialize}. For each retained wallet:\n *\n * - `'mnemonic'`: imports the mnemonic when provided and not already present,\n * then applies metadata to all groups.\n * - `'private-key'`: imports each retained group's key when provided and not\n * already present, then applies metadata.\n *\n * @param context - Import context providing state, messenger, and setters.\n * @param snapshot - The validated snapshot to import.\n */\nexport async function importState(\n context: ImportContext,\n snapshot: AccountTreeSnapshot,\n): Promise<void> {\n const payload = snapshot.serialize();\n\n for (const wallet of payload.wallets) {\n if (wallet.type === AccountWalletPayloadType.Mnemonic) {\n await importMnemonicWallet(context, wallet);\n } else {\n await importPrivateKeyWallet(context, wallet.groups);\n }\n }\n}\n"]}
1
+ {"version":3,"file":"import.mjs","sourceRoot":"","sources":["../../src/state/import.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n AccountWalletType,\n toAccountGroupId,\n toAccountWalletId,\n toMultichainAccountGroupId,\n} from '@metamask/account-api';\nimport type { AccountGroupId, AccountWalletId } from '@metamask/account-api';\nimport { getUUIDFromAddressOfNormalAccount } from '@metamask/accounts-controller';\nimport { HdKeyring } from '@metamask/eth-hd-keyring/v2';\nimport { EthAccountType } from '@metamask/keyring-api';\nimport { KeyringTypes } from '@metamask/keyring-controller';\n\nimport type {\n AccountTreeControllerMessenger,\n AccountTreeControllerState,\n} from '../types.js';\nimport type {\n AccountWalletEntropyObject,\n AccountWalletKeyringObject,\n} from '../wallet.js';\nimport { isMnemonicWalletObject } from './export.js';\nimport type {\n AccountWalletMnemonicGroupEntry,\n AccountWalletMnemonicPayload,\n AccountWalletPayloadId,\n AccountWalletPrivateKeyGroupEntry,\n} from './payload.js';\nimport {\n AccountWalletPayloadType,\n parsePayloadGroupId,\n toWalletPayloadId,\n} from './payload.js';\nimport type { AccountTreeSnapshot } from './snapshot.js';\nimport { decodeBytes } from './utils.js';\n\n/** Context required by {@link importState}. */\nexport type ImportContext = {\n getState: () => AccountTreeControllerState;\n messenger: AccountTreeControllerMessenger;\n setWalletName: (walletId: AccountWalletId, name: string) => void;\n /** Sets a group name. Implementations must resolve name conflicts automatically. */\n setAccountGroupName: (groupId: AccountGroupId, name: string) => void;\n setAccountGroupPinned: (groupId: AccountGroupId, pinned: boolean) => void;\n setAccountGroupHidden: (groupId: AccountGroupId, hidden: boolean) => void;\n};\n\n/**\n * Searches the local wallet tree for an entropy wallet whose stable payload ID\n * matches `payloadWalletId`. The entropy source ID is derived on-the-fly via\n * `KeyringController:withKeyringV2Unsafe` rather than relying on cached metadata.\n *\n * @param context - Import context.\n * @param payloadWalletId - Payload wallet ID to match against.\n * @returns The matching local entropy wallet, or `undefined` if not found.\n */\nasync function findLocalWalletMnemonicFromPayloadId(\n context: ImportContext,\n payloadWalletId: AccountWalletPayloadId,\n): Promise<AccountWalletEntropyObject | undefined> {\n const wallets = Object.values(context.getState().accountTree.wallets);\n\n for (const wallet of wallets) {\n if (isMnemonicWalletObject(wallet)) {\n const result = await context.messenger.call(\n 'KeyringController:withKeyringV2Unsafe',\n { id: wallet.metadata.entropy.id },\n async ({ keyring }) => {\n const hdKeyring = keyring as HdKeyring;\n\n return toWalletPayloadId(await hdKeyring.toEntropySourceId());\n },\n );\n\n const localPayloadId = result as AccountWalletPayloadId;\n if (localPayloadId === payloadWalletId) {\n return wallet;\n }\n }\n }\n\n return undefined;\n}\n\n/**\n * Returns the local entropy wallet for the given ID.\n *\n * @param context - Import context.\n * @param id - Local wallet ID.\n * @returns The entropy wallet object.\n * @throws If the wallet is not found or is not an entropy wallet.\n */\nfunction findLocalWalletMnemonicFromId(\n context: ImportContext,\n id: AccountWalletId,\n): AccountWalletEntropyObject {\n const localWallets = context.getState().accountTree.wallets;\n const localWallet = localWallets[id];\n\n if (!localWallet) {\n throw new Error(\n `Failed to import mnemonic wallet: wallet not found after creation`,\n );\n }\n if (!isMnemonicWalletObject(localWallet)) {\n throw new Error(\n `Failed to import mnemonic wallet: wallet is not of type 'mnemonic'`,\n );\n }\n return localWallet;\n}\n\n/**\n * Returns the local simple-keyring wallet, or `undefined` if none exists yet.\n *\n * The private-key wallet ID is static (derived solely from {@link KeyringTypes.simple}),\n * so no runtime argument is needed to locate it.\n *\n * @param context - Import context.\n * @returns The keyring wallet object, or `undefined` if not present in state.\n */\nfunction findLocalWalletPrivateKey(\n context: ImportContext,\n): AccountWalletKeyringObject | undefined {\n const localWalletId = toAccountWalletId(\n AccountWalletType.Keyring,\n KeyringTypes.simple,\n );\n const localWallet = context.getState().accountTree.wallets[localWalletId];\n return localWallet?.type === AccountWalletType.Keyring\n ? localWallet\n : undefined;\n}\n\n/**\n * Returns `true` if the given payload group is already present in the local simple-keyring wallet.\n *\n * @param localWallet - The local simple-keyring wallet, or `undefined` if none exists yet.\n * @param group - The payload group entry to check.\n * @returns Whether the group's account is already tracked locally.\n */\nfunction hasLocalGroupPrivateKey(\n localWallet: AccountWalletKeyringObject | undefined,\n group: AccountWalletPrivateKeyGroupEntry,\n): boolean {\n const address = parsePayloadGroupId(group.id).subId;\n\n const localWalletId = toAccountWalletId(\n AccountWalletType.Keyring,\n KeyringTypes.simple,\n );\n const localGroupId = toAccountGroupId(localWalletId, address);\n const localGroup = localWallet?.groups[localGroupId];\n\n const accountId = getUUIDFromAddressOfNormalAccount(address);\n\n return localGroup?.accounts.some((id) => id === accountId) ?? false;\n}\n\n/**\n * Applies name, pinned, and hidden metadata from a payload group entry to a local group.\n *\n * @param context - Import context providing the metadata setters.\n * @param localGroupId - Local group ID to update.\n * @param payloadGroupMetadata - Metadata from the payload group entry.\n */\nfunction setGroupMetadata(\n context: ImportContext,\n localGroupId: AccountGroupId,\n payloadGroupMetadata: AccountWalletMnemonicGroupEntry['metadata'],\n): void {\n context.setAccountGroupName(localGroupId, payloadGroupMetadata.name);\n context.setAccountGroupPinned(localGroupId, payloadGroupMetadata.pinned);\n context.setAccountGroupHidden(localGroupId, payloadGroupMetadata.hidden);\n}\n\n/**\n * Computes the contiguous ranges of group indices that are present in the payload\n * but absent from the local wallet, so they can be created in batches.\n *\n * @param localWallet - The local entropy wallet to check existing groups against.\n * @param payloadGroups - Ordered group entries from the payload.\n * @returns An array of `[fromGroupIndex, toGroupIndex]` ranges to create.\n */\nfunction getRangesFromPayloadGroups(\n localWallet: AccountWalletEntropyObject,\n payloadGroups: AccountWalletMnemonicGroupEntry[],\n): [number, number][] {\n let rangeIndex: number | undefined;\n const ranges: [number, number][] = [];\n\n // Keep track of the last payload group so we can close the final range if needed.\n let lastPayloadGroup: AccountWalletMnemonicGroupEntry | undefined;\n for (const payloadGroup of payloadGroups) {\n const localGroupId = toMultichainAccountGroupId(\n localWallet.id,\n payloadGroup.groupIndex,\n );\n\n if (localWallet.groups[localGroupId]) {\n if (rangeIndex !== undefined) {\n ranges.push([rangeIndex, payloadGroup.groupIndex - 1]);\n rangeIndex = undefined;\n }\n continue;\n }\n\n rangeIndex ??= payloadGroup.groupIndex;\n lastPayloadGroup = payloadGroup;\n }\n\n if (rangeIndex !== undefined && lastPayloadGroup !== undefined) {\n ranges.push([rangeIndex, lastPayloadGroup.groupIndex]);\n }\n\n return ranges;\n}\n\n/**\n * Applies a mnemonic wallet payload entry to the local state.\n *\n * If no local wallet with the same entropy source ID exists and a mnemonic is\n * present in the payload, a new HD wallet is created via\n * `MultichainAccountService:createMultichainAccountWallet`. Missing groups are\n * created in batches via `MultichainAccountService:createMultichainAccountGroups`.\n * Metadata (name, pinned, hidden) is applied to all groups afterward.\n *\n * @param context - Import context.\n * @param payloadWallet - The mnemonic wallet entry from the payload.\n */\nasync function importMnemonicWallet(\n context: ImportContext,\n payloadWallet: AccountWalletMnemonicPayload,\n): Promise<void> {\n // Find the local wallet with the same entropy source ID if it exists.\n let localWallet = await findLocalWalletMnemonicFromPayloadId(\n context,\n payloadWallet.id,\n );\n\n if (!localWallet) {\n if (!payloadWallet.value) {\n // No mnemonic in payload and wallet doesn't exist locally -- nothing to do.\n return;\n }\n\n // Import the mnemonic as a new HD wallet.\n const mnemonic = decodeBytes(payloadWallet.value);\n const { id } = await context.messenger.call(\n 'MultichainAccountService:createMultichainAccountWallet',\n { type: 'import', mnemonic },\n );\n\n // Event handlers fire synchronously, so the wallet is in the tree now.\n localWallet = findLocalWalletMnemonicFromId(context, id);\n }\n\n context.setWalletName(localWallet.id, payloadWallet.metadata.name);\n\n // Apply metadata to groups that are already present locally before attempting\n // to create missing ones. If createMultichainAccountGroups throws partway\n // through, these groups would otherwise be left without their payload metadata.\n const localExistingGroupIds = new Set(Object.keys(localWallet.groups));\n for (const payloadGroup of payloadWallet.groups) {\n const localGroupId = toMultichainAccountGroupId(\n localWallet.id,\n payloadGroup.groupIndex,\n );\n if (localExistingGroupIds.has(localGroupId)) {\n setGroupMetadata(context, localGroupId, payloadGroup.metadata);\n }\n }\n\n for (const range of getRangesFromPayloadGroups(\n localWallet,\n payloadWallet.groups,\n )) {\n await context.messenger.call(\n 'MultichainAccountService:createMultichainAccountGroups',\n {\n entropySource: localWallet.metadata.entropy.id,\n fromGroupIndex: range[0],\n toGroupIndex: range[1],\n },\n );\n }\n\n // Re-read wallet after groups creation.\n localWallet = findLocalWalletMnemonicFromId(context, localWallet.id);\n\n // Apply metadata to newly created groups. Skip groups that were already\n // handled above, and guard against groups that failed to be created so a\n // partial failure doesn't throw and abort the rest of the snapshot import.\n for (const payloadGroup of payloadWallet.groups) {\n const localGroupId = toMultichainAccountGroupId(\n localWallet.id,\n payloadGroup.groupIndex,\n );\n if (\n localExistingGroupIds.has(localGroupId) ||\n !localWallet.groups[localGroupId]\n ) {\n continue;\n }\n setGroupMetadata(context, localGroupId, payloadGroup.metadata);\n }\n}\n\n/**\n * Applies private-key wallet group entries from the payload to the local state.\n *\n * For each group the account address is derived from the payload group ID. If the\n * account does not yet exist locally and a private key is provided, it is imported\n * via `KeyringController:withKeyringV2` using the `'private-key:import'` constructor.\n * Metadata (name, pinned, hidden) is then applied to the local group.\n *\n * @param context - Import context.\n * @param payloadGroups - Private-key group entries from the payload.\n */\nasync function importPrivateKeyWallet(\n context: ImportContext,\n payloadGroups: AccountWalletPrivateKeyGroupEntry[],\n): Promise<void> {\n const localWalletId = toAccountWalletId(\n AccountWalletType.Keyring,\n KeyringTypes.simple,\n );\n\n // Only EVM EOA accounts are supported for now. Non-EVM private keys require\n // Snap-based import routing (ADR-0007), which is not yet implemented. Skip the\n // entire entry so payloads from future clients are accepted without crashing.\n // In the same pass, collect groups whose key is not yet present locally.\n const localWallet = findLocalWalletPrivateKey(context);\n const supportedGroups: AccountWalletPrivateKeyGroupEntry[] = [];\n const missingGroups: AccountWalletPrivateKeyGroupEntry[] = [];\n\n for (const group of payloadGroups) {\n const type = group.value?.type;\n if (type !== undefined && type !== EthAccountType.Eoa) {\n continue;\n }\n\n supportedGroups.push(group);\n\n if (\n !hasLocalGroupPrivateKey(localWallet, group) &&\n group.value !== undefined\n ) {\n missingGroups.push(group);\n }\n }\n\n // Import all missing keys in a single :withController call to avoid\n // triggering multiple `KeyringController:stateChanged` events.\n if (missingGroups.length > 0) {\n await context.messenger.call(\n 'KeyringController:withController',\n async (controller) => {\n // Find the existing simple keyring or create one if this is the\n // first private-key import (e.g. during onboarding).\n const existing = controller.keyrings.find(\n ({ keyring }) => keyring.type === KeyringTypes.simple,\n );\n const { keyringV2 } =\n existing ?? (await controller.addNewKeyring(KeyringTypes.simple));\n\n if (!keyringV2) {\n throw new Error('Simple keyring has no v2 interface');\n }\n\n for (const group of missingGroups) {\n // Safe to assert `group.value` is present because we filtered out groups\n // without a `value` above.\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const { privateKey: privateKeyBytes, encoding } = group.value!;\n const privateKey = new TextDecoder().decode(\n decodeBytes(privateKeyBytes),\n );\n const [account] = await keyringV2.createAccounts({\n type: 'private-key:import',\n accountType: EthAccountType.Eoa,\n privateKey,\n encoding,\n });\n\n if (!account) {\n throw new Error('Failed to import private key for account');\n }\n }\n },\n );\n }\n\n // Re-read state once after all imports, then apply metadata to every group\n // that now exists locally.\n for (const group of supportedGroups) {\n const address = parsePayloadGroupId(group.id).subId;\n const localGroupId = toAccountGroupId(localWalletId, address);\n const localGroup = findLocalWalletPrivateKey(context)?.groups[localGroupId];\n if (!localGroup) {\n continue;\n }\n setGroupMetadata(context, localGroup.id, group.metadata);\n }\n}\n\n/**\n * Applies an {@link AccountTreeSnapshot} to the current controller state.\n *\n * The snapshot must already have been validated — typically via\n * {@link AccountTreeSnapshot.deserialize}. For each retained wallet:\n *\n * - `'mnemonic'`: imports the mnemonic when provided and not already present,\n * then applies metadata to all groups.\n * - `'private-key'`: imports each retained group's key when provided and not\n * already present, then applies metadata.\n *\n * @param context - Import context providing state, messenger, and setters.\n * @param snapshot - The validated snapshot to import.\n */\nexport async function importState(\n context: ImportContext,\n snapshot: AccountTreeSnapshot,\n): Promise<void> {\n const payload = snapshot.serialize();\n\n for (const wallet of payload.wallets) {\n if (wallet.type === AccountWalletPayloadType.Mnemonic) {\n await importMnemonicWallet(context, wallet);\n } else {\n await importPrivateKeyWallet(context, wallet.groups);\n }\n }\n}\n"]}
@@ -84,12 +84,12 @@ const AccountWalletPrivateKeyValueStruct = (0, superstruct_1.object)({
84
84
  const AccountWalletMnemonicGroupEntryStruct = (0, superstruct_1.object)({
85
85
  id: AccountGroupPayloadIdStruct,
86
86
  groupIndex: (0, superstruct_1.integer)(),
87
- metadata: (0, superstruct_1.exactOptional)(AccountWalletGroupPayloadMetadataStruct),
87
+ metadata: AccountWalletGroupPayloadMetadataStruct,
88
88
  });
89
89
  const AccountWalletPrivateKeyGroupEntryStruct = (0, superstruct_1.object)({
90
90
  id: AccountGroupPayloadIdStruct,
91
91
  value: (0, superstruct_1.exactOptional)(AccountWalletPrivateKeyValueStruct),
92
- metadata: (0, superstruct_1.exactOptional)(AccountWalletGroupPayloadMetadataStruct),
92
+ metadata: AccountWalletGroupPayloadMetadataStruct,
93
93
  });
94
94
  // The `groups` array in a mnemonic wallet payload must have contiguous group indices starting at 0.
95
95
  const AccountWalletMnemonicGroupsStruct = (0, superstruct_1.refine)((0, superstruct_1.array)(AccountWalletMnemonicGroupEntryStruct), 'contiguous-group-indices', (groups) => {
@@ -109,13 +109,13 @@ const AccountWalletMnemonicPayloadStruct = (0, superstruct_1.object)({
109
109
  id: AccountWalletPayloadIdStruct,
110
110
  type: (0, superstruct_1.literal)(exports.AccountWalletPayloadType.Mnemonic),
111
111
  value: (0, superstruct_1.exactOptional)((0, superstruct_1.sensitive)(utils_js_1.BytesStruct)),
112
- metadata: (0, superstruct_1.exactOptional)(AccountWalletPayloadMetadataStruct),
112
+ metadata: AccountWalletPayloadMetadataStruct,
113
113
  groups: AccountWalletMnemonicGroupsStruct,
114
114
  });
115
115
  const AccountWalletPrivateKeyPayloadStruct = (0, superstruct_1.object)({
116
116
  id: AccountWalletPayloadIdStruct,
117
117
  type: (0, superstruct_1.literal)(exports.AccountWalletPayloadType.PrivateKey),
118
- metadata: (0, superstruct_1.exactOptional)(AccountWalletPayloadMetadataStruct),
118
+ metadata: AccountWalletPayloadMetadataStruct,
119
119
  groups: (0, superstruct_1.array)(AccountWalletPrivateKeyGroupEntryStruct),
120
120
  });
121
121
  const AccountTreeWalletEntryStruct = (0, superstruct_1.union)([
@@ -1 +1 @@
1
- {"version":3,"file":"payload.cjs","sourceRoot":"","sources":["../../src/state/payload.ts"],"names":[],"mappings":"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","sourcesContent":["import { KeyringAccountTypeStruct } from '@metamask/keyring-api';\nimport type { KeyringAccount } from '@metamask/keyring-api';\nimport {\n array,\n assert,\n boolean,\n define,\n enums,\n integer,\n literal,\n object,\n exactOptional,\n refine,\n sensitive,\n string,\n StructError,\n union,\n} from '@metamask/superstruct';\nimport type { Infer } from '@metamask/superstruct';\n\nimport type { DeepReadonly, EncodedBytes } from './utils.js';\nimport { BytesStruct, formatValidationErrorMessages } from './utils.js';\n\n/** Stable cross-device wallet identifier. Format: `wallet:<entropySourceId>`. */\nexport type AccountWalletPayloadId = `wallet:${string}`;\n\n/** Stable cross-device group identifier. Format: `wallet:<entropySourceId>/<groupSubId>`. */\nexport type AccountGroupPayloadId = `${AccountWalletPayloadId}/${string}`;\n\n/**\n * Parsed representation of an {@link AccountGroupPayloadId}.\n */\nexport type ParsedPayloadGroupId = {\n /** The wallet portion of the group ID. */\n walletId: AccountWalletPayloadId;\n /** The group-specific sub-ID (e.g. group index for mnemonic wallets, address for private-key wallets). */\n subId: string;\n};\n\nconst PAYLOAD_GROUP_ID_REGEX = /^(?<walletId>wallet:[^/]+)\\/(?<subId>.+)$/u;\n\n/**\n * Parses a payload group ID into its wallet ID and group sub-ID components.\n *\n * @param groupId - The payload group ID to parse.\n * @returns The parsed wallet ID and group sub-ID.\n * @throws If the group ID format is invalid.\n */\nexport function parsePayloadGroupId(\n groupId: AccountGroupPayloadId,\n): ParsedPayloadGroupId {\n const match = PAYLOAD_GROUP_ID_REGEX.exec(groupId);\n if (!match?.groups) {\n throw new Error(`Invalid payload group ID: \"${groupId}\"`);\n }\n return {\n walletId: match.groups.walletId as AccountWalletPayloadId,\n subId: match.groups.subId,\n };\n}\n\n/**\n * Wallet type discriminants used in serialized {@link AccountTreePayload} entries.\n *\n * Use these constants instead of raw string literals so callers get autocomplete\n * and a single source of truth for the discriminant values.\n */\nexport const AccountWalletPayloadType = {\n Mnemonic: 'mnemonic',\n PrivateKey: 'private-key',\n} as const;\n\nexport type AccountWalletPayloadType =\n (typeof AccountWalletPayloadType)[keyof typeof AccountWalletPayloadType];\n\n/** Wallet-level metadata carried in every payload wallet entry. */\nexport type AccountWalletPayloadMetadata = { name: string };\n\n/** Group-level metadata carried in every payload group entry. */\nexport type AccountWalletGroupPayloadMetadata = {\n name: string;\n pinned: boolean;\n hidden: boolean;\n};\n\n/** A single group entry inside an {@link AccountWalletMnemonicPayload}. */\nexport type AccountWalletMnemonicGroupEntry = {\n /** Stable group payload ID. Format: `<walletPayloadId>/<groupIndex>`. */\n id: AccountGroupPayloadId;\n /** BIP-44 account index this group was derived at. */\n groupIndex: number;\n metadata?: AccountWalletGroupPayloadMetadata;\n};\n\n/**\n * Encoding formats for exported private key material.\n */\nexport const AccountWalletPrivateKeyEncoding = {\n Hexadecimal: 'hexadecimal',\n Base58: 'base58',\n Base32: 'base32',\n} as const;\n\nexport type AccountWalletPrivateKeyEncoding =\n (typeof AccountWalletPrivateKeyEncoding)[keyof typeof AccountWalletPrivateKeyEncoding];\n\n/** A single group entry inside an {@link AccountWalletPrivateKeyPayload}. */\nexport type AccountWalletPrivateKeyGroupEntry = {\n /** Stable group payload ID. Format: `wallet:private-key/<address>`. */\n id: AccountGroupPayloadId;\n /**\n * Private key material. Shape matches `ExportedAccount` from `@metamask/keyring-api/v2`\n * so the importer knows how to decode the key without additional out-of-band information.\n * Absent in metadata-only exports.\n */\n value?: {\n privateKey: EncodedBytes;\n encoding: AccountWalletPrivateKeyEncoding;\n /**\n * Account type from `KeyringAccountType` (e.g. `'eip155:eoa'`, `'bip122:p2wpkh'`).\n * Absent for EVM accounts -- import via `SimpleKeyring`.\n * Present for non-EVM accounts -- routing to the BIP-44 Snap handling this type is not yet implemented.\n */\n type?: KeyringAccount['type'];\n };\n metadata?: AccountWalletGroupPayloadMetadata;\n};\n\n/** Payload entry for an HD (entropy) wallet and its derived account groups. */\nexport type AccountWalletMnemonicPayload = {\n id: AccountWalletPayloadId;\n type: typeof AccountWalletPayloadType.Mnemonic;\n /** BIP-39 mnemonic phrase encoded as bytes. Absent in metadata-only exports. */\n value?: EncodedBytes;\n metadata?: AccountWalletPayloadMetadata;\n groups: AccountWalletMnemonicGroupEntry[];\n};\n\n/**\n * Payload entry for all imported private-key accounts.\n *\n * All local simple-keyring wallets are merged into this single entry;\n * each account is represented as a separate group entry keyed by address.\n */\nexport type AccountWalletPrivateKeyPayload = {\n id: AccountWalletPayloadId;\n type: typeof AccountWalletPayloadType.PrivateKey;\n metadata?: AccountWalletPayloadMetadata;\n groups: AccountWalletPrivateKeyGroupEntry[];\n};\n\n/** Union of all wallet entry types that can appear in an {@link AccountTreePayload}. */\nexport type AccountTreeWalletEntry =\n | AccountWalletMnemonicPayload\n | AccountWalletPrivateKeyPayload;\n\n/** Portable snapshot of the full account tree state (flat versioned format). */\nexport type AccountTreePayload = {\n version: number;\n wallets: AccountTreeWalletEntry[];\n};\n\n/**\n * Deeply read-only wallet view passed to {@link AccountTreeSnapshot.filterWallets}\n * and {@link AccountTreeSnapshot.filterAllGroups} predicates.\n *\n * Entries are deep-cloned and deep-frozen when a snapshot is constructed, so\n * callers cannot mutate wallet IDs, types, secrets, metadata, or groups.\n */\nexport type AccountTreeSnapshotWallet = DeepReadonly<\n AccountWalletMnemonicPayload | AccountWalletPrivateKeyPayload\n>;\n\n/**\n * Deeply read-only group view passed to {@link AccountTreeSnapshot.filterGroups}\n * and {@link AccountTreeSnapshot.filterAllGroups} predicates.\n *\n * Entries are deep-cloned and deep-frozen when a snapshot is constructed, so\n * callers cannot mutate group IDs, secrets, metadata, or parent wallet references.\n */\nexport type AccountTreeSnapshotGroup = DeepReadonly<\n AccountWalletMnemonicGroupEntry | AccountWalletPrivateKeyGroupEntry\n>;\n\n/**\n * Constructs an {@link AccountWalletPayloadId} from an entropy source ID.\n *\n * @param entropySourceId - Stable entropy source ID returned by `HdKeyring.toEntropySourceId()`.\n * @returns The portable wallet payload ID.\n */\nexport function toWalletPayloadId(\n entropySourceId: string,\n): AccountWalletPayloadId {\n return `wallet:${entropySourceId}`;\n}\n\n/**\n * Constructs an {@link AccountGroupPayloadId} from a wallet payload ID and a sub-ID.\n *\n * @param walletId - The wallet payload ID this group belongs to.\n * @param subId - The group-specific sub-ID (e.g. group index for mnemonic wallets, address for private-key wallets).\n * @returns The portable group payload ID.\n */\nexport function toGroupPayloadId(\n walletId: AccountWalletPayloadId,\n subId: string | number,\n): AccountGroupPayloadId {\n return `${walletId}/${subId}`;\n}\n\n/** Options accepted by {@link AccountTreeController.exportState}. */\nexport type ExportStateOptions =\n | {\n /** When `true`, secrets (mnemonic / private keys) are included in the snapshot. */\n includeSecrets: true;\n /** Password verified against the vault before any secret is read. */\n password: string;\n }\n | {\n /** When `false` or omitted, only metadata is exported — no password needed. */\n includeSecrets?: false;\n password?: never;\n };\n\nconst AccountWalletPayloadIdStruct = define<AccountWalletPayloadId>(\n 'AccountWalletPayloadId',\n (value) =>\n typeof value === 'string' && value.startsWith('wallet:')\n ? true\n : 'Expected a wallet payload ID starting with \"wallet:\"',\n);\n\nconst AccountGroupPayloadIdStruct = define<AccountGroupPayloadId>(\n 'AccountGroupPayloadId',\n (value) =>\n typeof value === 'string' && PAYLOAD_GROUP_ID_REGEX.test(value)\n ? true\n : 'Expected a group payload ID in the form \"wallet:<id>/<subId>\"',\n);\n\nconst AccountWalletPayloadMetadataStruct = object({\n name: string(),\n});\n\nconst AccountWalletGroupPayloadMetadataStruct = object({\n name: string(),\n pinned: boolean(),\n hidden: boolean(),\n});\n\nconst AccountWalletPrivateKeyValueStruct = object({\n privateKey: sensitive(BytesStruct),\n encoding: enums(Object.values(AccountWalletPrivateKeyEncoding)),\n type: exactOptional(KeyringAccountTypeStruct),\n});\n\nconst AccountWalletMnemonicGroupEntryStruct = object({\n id: AccountGroupPayloadIdStruct,\n groupIndex: integer(),\n metadata: exactOptional(AccountWalletGroupPayloadMetadataStruct),\n});\n\nconst AccountWalletPrivateKeyGroupEntryStruct = object({\n id: AccountGroupPayloadIdStruct,\n value: exactOptional(AccountWalletPrivateKeyValueStruct),\n metadata: exactOptional(AccountWalletGroupPayloadMetadataStruct),\n});\n\n// The `groups` array in a mnemonic wallet payload must have contiguous group indices starting at 0.\nconst AccountWalletMnemonicGroupsStruct = refine(\n array(AccountWalletMnemonicGroupEntryStruct),\n 'contiguous-group-indices',\n (groups) => {\n let prev: AccountWalletMnemonicGroupEntry | undefined;\n for (const curr of groups) {\n if (prev === undefined && curr.groupIndex !== 0) {\n return `group indices must start at 0; got ${curr.groupIndex}`;\n }\n if (prev !== undefined && curr.groupIndex !== prev.groupIndex + 1) {\n return `group indices must be contiguous and sorted; found gap between index ${prev.groupIndex} and ${curr.groupIndex}`;\n }\n prev = curr;\n }\n return true;\n },\n);\n\nconst AccountWalletMnemonicPayloadStruct = object({\n id: AccountWalletPayloadIdStruct,\n type: literal(AccountWalletPayloadType.Mnemonic),\n value: exactOptional(sensitive(BytesStruct)),\n metadata: exactOptional(AccountWalletPayloadMetadataStruct),\n groups: AccountWalletMnemonicGroupsStruct,\n});\n\nconst AccountWalletPrivateKeyPayloadStruct = object({\n id: AccountWalletPayloadIdStruct,\n type: literal(AccountWalletPayloadType.PrivateKey),\n metadata: exactOptional(AccountWalletPayloadMetadataStruct),\n groups: array(AccountWalletPrivateKeyGroupEntryStruct),\n});\n\nconst AccountTreeWalletEntryStruct = union([\n AccountWalletMnemonicPayloadStruct,\n AccountWalletPrivateKeyPayloadStruct,\n]);\n\n/** Current version of the {@link AccountTreePayload} format. */\nexport const ACCOUNT_TREE_PAYLOAD_CURRENT_VERSION = 1;\n\n/**\n * Superstruct schema for a fully versioned {@link AccountTreePayload}.\n *\n * Pins `version` to exactly {@link ACCOUNT_TREE_PAYLOAD_CURRENT_VERSION} so\n * payloads from newer clients (v2+) are rejected rather than silently\n * mis-handled as v1. Once a migration framework is wired up in\n * {@link AccountTreeSnapshot.deserialize}, older versions will be up-migrated\n * before this struct is checked, and newer versions will require a new struct.\n *\n * Secret fields (`value`, `privateKey`) use the Superstruct `sensitive()`\n * wrapper so validation failures redact secrets from error output.\n */\nexport const AccountTreePayloadStruct = object({\n version: literal(ACCOUNT_TREE_PAYLOAD_CURRENT_VERSION),\n wallets: array(AccountTreeWalletEntryStruct),\n});\n\n/** Inferred TypeScript type for a value matching {@link AccountTreePayloadStruct}. */\nexport type AccountTreePayloadStructType = Infer<\n typeof AccountTreePayloadStruct\n>;\n\n/**\n * Asserts that `value` conforms to the v1 {@link AccountTreePayload} schema.\n *\n * Prefer {@link AccountTreeSnapshot.deserialize} at transport boundaries so\n * validation stays paired with snapshot construction. Use this helper when you\n * already hold a parsed object and need to assert its shape before further\n * processing.\n *\n * @param value - Value to validate.\n * @throws If `value` is not a valid v1 payload, including unsupported wallet types.\n */\nexport function assertAccountTreePayload(\n value: unknown,\n): asserts value is AccountTreePayload {\n try {\n // AccountTreePayloadStruct pins version to ACCOUNT_TREE_PAYLOAD_CURRENT_VERSION,\n // so unknown future versions are rejected here rather than silently mis-handled.\n assert(value, AccountTreePayloadStruct);\n } catch (error) {\n if (error instanceof StructError) {\n throw new Error(\n `Invalid AccountTreePayload: ${formatValidationErrorMessages(error)}`,\n );\n }\n /* istanbul ignore next */\n throw error;\n }\n}\n"]}
1
+ {"version":3,"file":"payload.cjs","sourceRoot":"","sources":["../../src/state/payload.ts"],"names":[],"mappings":"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","sourcesContent":["import { KeyringAccountTypeStruct } from '@metamask/keyring-api';\nimport type { KeyringAccount } from '@metamask/keyring-api';\nimport {\n array,\n assert,\n boolean,\n define,\n enums,\n integer,\n literal,\n object,\n exactOptional,\n refine,\n sensitive,\n string,\n StructError,\n union,\n} from '@metamask/superstruct';\nimport type { Infer } from '@metamask/superstruct';\n\nimport type { DeepReadonly, EncodedBytes } from './utils.js';\nimport { BytesStruct, formatValidationErrorMessages } from './utils.js';\n\n/** Stable cross-device wallet identifier. Format: `wallet:<entropySourceId>`. */\nexport type AccountWalletPayloadId = `wallet:${string}`;\n\n/** Stable cross-device group identifier. Format: `wallet:<entropySourceId>/<groupSubId>`. */\nexport type AccountGroupPayloadId = `${AccountWalletPayloadId}/${string}`;\n\n/**\n * Parsed representation of an {@link AccountGroupPayloadId}.\n */\nexport type ParsedPayloadGroupId = {\n /** The wallet portion of the group ID. */\n walletId: AccountWalletPayloadId;\n /** The group-specific sub-ID (e.g. group index for mnemonic wallets, address for private-key wallets). */\n subId: string;\n};\n\nconst PAYLOAD_GROUP_ID_REGEX = /^(?<walletId>wallet:[^/]+)\\/(?<subId>.+)$/u;\n\n/**\n * Parses a payload group ID into its wallet ID and group sub-ID components.\n *\n * @param groupId - The payload group ID to parse.\n * @returns The parsed wallet ID and group sub-ID.\n * @throws If the group ID format is invalid.\n */\nexport function parsePayloadGroupId(\n groupId: AccountGroupPayloadId,\n): ParsedPayloadGroupId {\n const match = PAYLOAD_GROUP_ID_REGEX.exec(groupId);\n if (!match?.groups) {\n throw new Error(`Invalid payload group ID: \"${groupId}\"`);\n }\n return {\n walletId: match.groups.walletId as AccountWalletPayloadId,\n subId: match.groups.subId,\n };\n}\n\n/**\n * Wallet type discriminants used in serialized {@link AccountTreePayload} entries.\n *\n * Use these constants instead of raw string literals so callers get autocomplete\n * and a single source of truth for the discriminant values.\n */\nexport const AccountWalletPayloadType = {\n Mnemonic: 'mnemonic',\n PrivateKey: 'private-key',\n} as const;\n\nexport type AccountWalletPayloadType =\n (typeof AccountWalletPayloadType)[keyof typeof AccountWalletPayloadType];\n\n/** Wallet-level metadata carried in every payload wallet entry. */\nexport type AccountWalletPayloadMetadata = { name: string };\n\n/** Group-level metadata carried in every payload group entry. */\nexport type AccountWalletGroupPayloadMetadata = {\n name: string;\n pinned: boolean;\n hidden: boolean;\n};\n\n/** A single group entry inside an {@link AccountWalletMnemonicPayload}. */\nexport type AccountWalletMnemonicGroupEntry = {\n /** Stable group payload ID. Format: `<walletPayloadId>/<groupIndex>`. */\n id: AccountGroupPayloadId;\n /** BIP-44 account index this group was derived at. */\n groupIndex: number;\n metadata: AccountWalletGroupPayloadMetadata;\n};\n\n/**\n * Encoding formats for exported private key material.\n */\nexport const AccountWalletPrivateKeyEncoding = {\n Hexadecimal: 'hexadecimal',\n Base58: 'base58',\n Base32: 'base32',\n} as const;\n\nexport type AccountWalletPrivateKeyEncoding =\n (typeof AccountWalletPrivateKeyEncoding)[keyof typeof AccountWalletPrivateKeyEncoding];\n\n/** A single group entry inside an {@link AccountWalletPrivateKeyPayload}. */\nexport type AccountWalletPrivateKeyGroupEntry = {\n /** Stable group payload ID. Format: `wallet:private-key/<address>`. */\n id: AccountGroupPayloadId;\n /**\n * Private key material. Shape matches `ExportedAccount` from `@metamask/keyring-api/v2`\n * so the importer knows how to decode the key without additional out-of-band information.\n * Absent in metadata-only exports.\n */\n value?: {\n privateKey: EncodedBytes;\n encoding: AccountWalletPrivateKeyEncoding;\n /**\n * Account type from `KeyringAccountType` (e.g. `'eip155:eoa'`, `'bip122:p2wpkh'`).\n * Absent for EVM accounts -- import via `SimpleKeyring`.\n * Present for non-EVM accounts -- routing to the BIP-44 Snap handling this type is not yet implemented.\n */\n type?: KeyringAccount['type'];\n };\n metadata: AccountWalletGroupPayloadMetadata;\n};\n\n/** Payload entry for an HD (entropy) wallet and its derived account groups. */\nexport type AccountWalletMnemonicPayload = {\n id: AccountWalletPayloadId;\n type: typeof AccountWalletPayloadType.Mnemonic;\n /** BIP-39 mnemonic phrase encoded as bytes. Absent in metadata-only exports. */\n value?: EncodedBytes;\n metadata: AccountWalletPayloadMetadata;\n groups: AccountWalletMnemonicGroupEntry[];\n};\n\n/**\n * Payload entry for all imported private-key accounts.\n *\n * All local simple-keyring wallets are merged into this single entry;\n * each account is represented as a separate group entry keyed by address.\n */\nexport type AccountWalletPrivateKeyPayload = {\n id: AccountWalletPayloadId;\n type: typeof AccountWalletPayloadType.PrivateKey;\n metadata: AccountWalletPayloadMetadata;\n groups: AccountWalletPrivateKeyGroupEntry[];\n};\n\n/** Union of all wallet entry types that can appear in an {@link AccountTreePayload}. */\nexport type AccountTreeWalletEntry =\n | AccountWalletMnemonicPayload\n | AccountWalletPrivateKeyPayload;\n\n/** Portable snapshot of the full account tree state (flat versioned format). */\nexport type AccountTreePayload = {\n version: number;\n wallets: AccountTreeWalletEntry[];\n};\n\n/**\n * Deeply read-only wallet view passed to {@link AccountTreeSnapshot.filterWallets}\n * and {@link AccountTreeSnapshot.filterAllGroups} predicates.\n *\n * Entries are deep-cloned and deep-frozen when a snapshot is constructed, so\n * callers cannot mutate wallet IDs, types, secrets, metadata, or groups.\n */\nexport type AccountTreeSnapshotWallet = DeepReadonly<\n AccountWalletMnemonicPayload | AccountWalletPrivateKeyPayload\n>;\n\n/**\n * Deeply read-only group view passed to {@link AccountTreeSnapshot.filterGroups}\n * and {@link AccountTreeSnapshot.filterAllGroups} predicates.\n *\n * Entries are deep-cloned and deep-frozen when a snapshot is constructed, so\n * callers cannot mutate group IDs, secrets, metadata, or parent wallet references.\n */\nexport type AccountTreeSnapshotGroup = DeepReadonly<\n AccountWalletMnemonicGroupEntry | AccountWalletPrivateKeyGroupEntry\n>;\n\n/**\n * Constructs an {@link AccountWalletPayloadId} from an entropy source ID.\n *\n * @param entropySourceId - Stable entropy source ID returned by `HdKeyring.toEntropySourceId()`.\n * @returns The portable wallet payload ID.\n */\nexport function toWalletPayloadId(\n entropySourceId: string,\n): AccountWalletPayloadId {\n return `wallet:${entropySourceId}`;\n}\n\n/**\n * Constructs an {@link AccountGroupPayloadId} from a wallet payload ID and a sub-ID.\n *\n * @param walletId - The wallet payload ID this group belongs to.\n * @param subId - The group-specific sub-ID (e.g. group index for mnemonic wallets, address for private-key wallets).\n * @returns The portable group payload ID.\n */\nexport function toGroupPayloadId(\n walletId: AccountWalletPayloadId,\n subId: string | number,\n): AccountGroupPayloadId {\n return `${walletId}/${subId}`;\n}\n\n/** Options accepted by {@link AccountTreeController.exportState}. */\nexport type ExportStateOptions =\n | {\n /** When `true`, secrets (mnemonic / private keys) are included in the snapshot. */\n includeSecrets: true;\n /** Password verified against the vault before any secret is read. */\n password: string;\n }\n | {\n /** When `false` or omitted, only metadata is exported — no password needed. */\n includeSecrets?: false;\n password?: never;\n };\n\nconst AccountWalletPayloadIdStruct = define<AccountWalletPayloadId>(\n 'AccountWalletPayloadId',\n (value) =>\n typeof value === 'string' && value.startsWith('wallet:')\n ? true\n : 'Expected a wallet payload ID starting with \"wallet:\"',\n);\n\nconst AccountGroupPayloadIdStruct = define<AccountGroupPayloadId>(\n 'AccountGroupPayloadId',\n (value) =>\n typeof value === 'string' && PAYLOAD_GROUP_ID_REGEX.test(value)\n ? true\n : 'Expected a group payload ID in the form \"wallet:<id>/<subId>\"',\n);\n\nconst AccountWalletPayloadMetadataStruct = object({\n name: string(),\n});\n\nconst AccountWalletGroupPayloadMetadataStruct = object({\n name: string(),\n pinned: boolean(),\n hidden: boolean(),\n});\n\nconst AccountWalletPrivateKeyValueStruct = object({\n privateKey: sensitive(BytesStruct),\n encoding: enums(Object.values(AccountWalletPrivateKeyEncoding)),\n type: exactOptional(KeyringAccountTypeStruct),\n});\n\nconst AccountWalletMnemonicGroupEntryStruct = object({\n id: AccountGroupPayloadIdStruct,\n groupIndex: integer(),\n metadata: AccountWalletGroupPayloadMetadataStruct,\n});\n\nconst AccountWalletPrivateKeyGroupEntryStruct = object({\n id: AccountGroupPayloadIdStruct,\n value: exactOptional(AccountWalletPrivateKeyValueStruct),\n metadata: AccountWalletGroupPayloadMetadataStruct,\n});\n\n// The `groups` array in a mnemonic wallet payload must have contiguous group indices starting at 0.\nconst AccountWalletMnemonicGroupsStruct = refine(\n array(AccountWalletMnemonicGroupEntryStruct),\n 'contiguous-group-indices',\n (groups) => {\n let prev: AccountWalletMnemonicGroupEntry | undefined;\n for (const curr of groups) {\n if (prev === undefined && curr.groupIndex !== 0) {\n return `group indices must start at 0; got ${curr.groupIndex}`;\n }\n if (prev !== undefined && curr.groupIndex !== prev.groupIndex + 1) {\n return `group indices must be contiguous and sorted; found gap between index ${prev.groupIndex} and ${curr.groupIndex}`;\n }\n prev = curr;\n }\n return true;\n },\n);\n\nconst AccountWalletMnemonicPayloadStruct = object({\n id: AccountWalletPayloadIdStruct,\n type: literal(AccountWalletPayloadType.Mnemonic),\n value: exactOptional(sensitive(BytesStruct)),\n metadata: AccountWalletPayloadMetadataStruct,\n groups: AccountWalletMnemonicGroupsStruct,\n});\n\nconst AccountWalletPrivateKeyPayloadStruct = object({\n id: AccountWalletPayloadIdStruct,\n type: literal(AccountWalletPayloadType.PrivateKey),\n metadata: AccountWalletPayloadMetadataStruct,\n groups: array(AccountWalletPrivateKeyGroupEntryStruct),\n});\n\nconst AccountTreeWalletEntryStruct = union([\n AccountWalletMnemonicPayloadStruct,\n AccountWalletPrivateKeyPayloadStruct,\n]);\n\n/** Current version of the {@link AccountTreePayload} format. */\nexport const ACCOUNT_TREE_PAYLOAD_CURRENT_VERSION = 1;\n\n/**\n * Superstruct schema for a fully versioned {@link AccountTreePayload}.\n *\n * Pins `version` to exactly {@link ACCOUNT_TREE_PAYLOAD_CURRENT_VERSION} so\n * payloads from newer clients (v2+) are rejected rather than silently\n * mis-handled as v1. Once a migration framework is wired up in\n * {@link AccountTreeSnapshot.deserialize}, older versions will be up-migrated\n * before this struct is checked, and newer versions will require a new struct.\n *\n * Secret fields (`value`, `privateKey`) use the Superstruct `sensitive()`\n * wrapper so validation failures redact secrets from error output.\n */\nexport const AccountTreePayloadStruct = object({\n version: literal(ACCOUNT_TREE_PAYLOAD_CURRENT_VERSION),\n wallets: array(AccountTreeWalletEntryStruct),\n});\n\n/** Inferred TypeScript type for a value matching {@link AccountTreePayloadStruct}. */\nexport type AccountTreePayloadStructType = Infer<\n typeof AccountTreePayloadStruct\n>;\n\n/**\n * Asserts that `value` conforms to the v1 {@link AccountTreePayload} schema.\n *\n * Prefer {@link AccountTreeSnapshot.deserialize} at transport boundaries so\n * validation stays paired with snapshot construction. Use this helper when you\n * already hold a parsed object and need to assert its shape before further\n * processing.\n *\n * @param value - Value to validate.\n * @throws If `value` is not a valid v1 payload, including unsupported wallet types.\n */\nexport function assertAccountTreePayload(\n value: unknown,\n): asserts value is AccountTreePayload {\n try {\n // AccountTreePayloadStruct pins version to ACCOUNT_TREE_PAYLOAD_CURRENT_VERSION,\n // so unknown future versions are rejected here rather than silently mis-handled.\n assert(value, AccountTreePayloadStruct);\n } catch (error) {\n if (error instanceof StructError) {\n throw new Error(\n `Invalid AccountTreePayload: ${formatValidationErrorMessages(error)}`,\n );\n }\n /* istanbul ignore next */\n throw error;\n }\n}\n"]}
@@ -49,7 +49,7 @@ export type AccountWalletMnemonicGroupEntry = {
49
49
  id: AccountGroupPayloadId;
50
50
  /** BIP-44 account index this group was derived at. */
51
51
  groupIndex: number;
52
- metadata?: AccountWalletGroupPayloadMetadata;
52
+ metadata: AccountWalletGroupPayloadMetadata;
53
53
  };
54
54
  /**
55
55
  * Encoding formats for exported private key material.
@@ -79,7 +79,7 @@ export type AccountWalletPrivateKeyGroupEntry = {
79
79
  */
80
80
  type?: KeyringAccount['type'];
81
81
  };
82
- metadata?: AccountWalletGroupPayloadMetadata;
82
+ metadata: AccountWalletGroupPayloadMetadata;
83
83
  };
84
84
  /** Payload entry for an HD (entropy) wallet and its derived account groups. */
85
85
  export type AccountWalletMnemonicPayload = {
@@ -87,7 +87,7 @@ export type AccountWalletMnemonicPayload = {
87
87
  type: typeof AccountWalletPayloadType.Mnemonic;
88
88
  /** BIP-39 mnemonic phrase encoded as bytes. Absent in metadata-only exports. */
89
89
  value?: EncodedBytes;
90
- metadata?: AccountWalletPayloadMetadata;
90
+ metadata: AccountWalletPayloadMetadata;
91
91
  groups: AccountWalletMnemonicGroupEntry[];
92
92
  };
93
93
  /**
@@ -99,7 +99,7 @@ export type AccountWalletMnemonicPayload = {
99
99
  export type AccountWalletPrivateKeyPayload = {
100
100
  id: AccountWalletPayloadId;
101
101
  type: typeof AccountWalletPayloadType.PrivateKey;
102
- metadata?: AccountWalletPayloadMetadata;
102
+ metadata: AccountWalletPayloadMetadata;
103
103
  groups: AccountWalletPrivateKeyGroupEntry[];
104
104
  };
105
105
  /** Union of all wallet entry types that can appear in an {@link AccountTreePayload}. */
@@ -169,38 +169,38 @@ export declare const AccountTreePayloadStruct: import("@metamask/superstruct").S
169
169
  wallets: ({
170
170
  id: `wallet:${string}`;
171
171
  type: "mnemonic";
172
+ metadata: {
173
+ name: string;
174
+ };
172
175
  groups: {
173
176
  id: `wallet:${string}/${string}`;
174
- groupIndex: number;
175
- metadata?: {
177
+ metadata: {
176
178
  name: string;
177
179
  pinned: boolean;
178
180
  hidden: boolean;
179
- } | undefined;
181
+ };
182
+ groupIndex: number;
180
183
  }[];
181
- metadata?: {
182
- name: string;
183
- } | undefined;
184
184
  value?: EncodedBytes | undefined;
185
185
  } | {
186
186
  id: `wallet:${string}`;
187
187
  type: "private-key";
188
+ metadata: {
189
+ name: string;
190
+ };
188
191
  groups: {
189
192
  id: `wallet:${string}/${string}`;
190
- metadata?: {
193
+ metadata: {
191
194
  name: string;
192
195
  pinned: boolean;
193
196
  hidden: boolean;
194
- } | undefined;
197
+ };
195
198
  value?: {
196
199
  privateKey: EncodedBytes;
197
200
  encoding: "hexadecimal" | "base58" | "base32";
198
201
  type?: "eip155:eoa" | "eip155:erc4337" | "bip122:p2pkh" | "bip122:p2sh" | "bip122:p2wpkh" | "bip122:p2tr" | "solana:data-account" | "tron:eoa" | "stellar:account" | "any:account" | undefined;
199
202
  } | undefined;
200
203
  }[];
201
- metadata?: {
202
- name: string;
203
- } | undefined;
204
204
  })[];
205
205
  version: 1;
206
206
  }, {
@@ -208,73 +208,73 @@ export declare const AccountTreePayloadStruct: import("@metamask/superstruct").S
208
208
  wallets: import("@metamask/superstruct").Struct<({
209
209
  id: `wallet:${string}`;
210
210
  type: "mnemonic";
211
+ metadata: {
212
+ name: string;
213
+ };
211
214
  groups: {
212
215
  id: `wallet:${string}/${string}`;
213
- groupIndex: number;
214
- metadata?: {
216
+ metadata: {
215
217
  name: string;
216
218
  pinned: boolean;
217
219
  hidden: boolean;
218
- } | undefined;
220
+ };
221
+ groupIndex: number;
219
222
  }[];
220
- metadata?: {
221
- name: string;
222
- } | undefined;
223
223
  value?: EncodedBytes | undefined;
224
224
  } | {
225
225
  id: `wallet:${string}`;
226
226
  type: "private-key";
227
+ metadata: {
228
+ name: string;
229
+ };
227
230
  groups: {
228
231
  id: `wallet:${string}/${string}`;
229
- metadata?: {
232
+ metadata: {
230
233
  name: string;
231
234
  pinned: boolean;
232
235
  hidden: boolean;
233
- } | undefined;
236
+ };
234
237
  value?: {
235
238
  privateKey: EncodedBytes;
236
239
  encoding: "hexadecimal" | "base58" | "base32";
237
240
  type?: "eip155:eoa" | "eip155:erc4337" | "bip122:p2pkh" | "bip122:p2sh" | "bip122:p2wpkh" | "bip122:p2tr" | "solana:data-account" | "tron:eoa" | "stellar:account" | "any:account" | undefined;
238
241
  } | undefined;
239
242
  }[];
240
- metadata?: {
241
- name: string;
242
- } | undefined;
243
243
  })[], import("@metamask/superstruct").Struct<{
244
244
  id: `wallet:${string}`;
245
245
  type: "mnemonic";
246
+ metadata: {
247
+ name: string;
248
+ };
246
249
  groups: {
247
250
  id: `wallet:${string}/${string}`;
248
- groupIndex: number;
249
- metadata?: {
251
+ metadata: {
250
252
  name: string;
251
253
  pinned: boolean;
252
254
  hidden: boolean;
253
- } | undefined;
255
+ };
256
+ groupIndex: number;
254
257
  }[];
255
- metadata?: {
256
- name: string;
257
- } | undefined;
258
258
  value?: EncodedBytes | undefined;
259
259
  } | {
260
260
  id: `wallet:${string}`;
261
261
  type: "private-key";
262
+ metadata: {
263
+ name: string;
264
+ };
262
265
  groups: {
263
266
  id: `wallet:${string}/${string}`;
264
- metadata?: {
267
+ metadata: {
265
268
  name: string;
266
269
  pinned: boolean;
267
270
  hidden: boolean;
268
- } | undefined;
271
+ };
269
272
  value?: {
270
273
  privateKey: EncodedBytes;
271
274
  encoding: "hexadecimal" | "base58" | "base32";
272
275
  type?: "eip155:eoa" | "eip155:erc4337" | "bip122:p2pkh" | "bip122:p2sh" | "bip122:p2wpkh" | "bip122:p2tr" | "solana:data-account" | "tron:eoa" | "stellar:account" | "any:account" | undefined;
273
276
  } | undefined;
274
277
  }[];
275
- metadata?: {
276
- name: string;
277
- } | undefined;
278
278
  }, null>>;
279
279
  }>;
280
280
  /** Inferred TypeScript type for a value matching {@link AccountTreePayloadStruct}. */