@metamask-previews/account-tree-controller 7.6.1-preview-e57e5c3dc → 7.6.1-preview-c449b8da8
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 +10 -0
- package/dist/AccountTreeController-method-action-types.cjs.map +1 -1
- package/dist/AccountTreeController-method-action-types.d.cts +34 -1
- package/dist/AccountTreeController-method-action-types.d.cts.map +1 -1
- package/dist/AccountTreeController-method-action-types.d.mts +34 -1
- package/dist/AccountTreeController-method-action-types.d.mts.map +1 -1
- package/dist/AccountTreeController-method-action-types.mjs.map +1 -1
- package/dist/AccountTreeController.cjs +42 -0
- package/dist/AccountTreeController.cjs.map +1 -1
- package/dist/AccountTreeController.d.cts +29 -0
- package/dist/AccountTreeController.d.cts.map +1 -1
- package/dist/AccountTreeController.d.mts +29 -0
- package/dist/AccountTreeController.d.mts.map +1 -1
- package/dist/AccountTreeController.mjs +42 -0
- package/dist/AccountTreeController.mjs.map +1 -1
- package/dist/index.cjs +10 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +5 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +3 -0
- package/dist/index.mjs.map +1 -1
- package/dist/state/export.cjs +214 -0
- package/dist/state/export.cjs.map +1 -0
- package/dist/state/export.d.cts +40 -0
- package/dist/state/export.d.cts.map +1 -0
- package/dist/state/export.d.mts +40 -0
- package/dist/state/export.d.mts.map +1 -0
- package/dist/state/export.mjs +208 -0
- package/dist/state/export.mjs.map +1 -0
- package/dist/state/id-map.cjs +65 -0
- package/dist/state/id-map.cjs.map +1 -0
- package/dist/state/id-map.d.cts +41 -0
- package/dist/state/id-map.d.cts.map +1 -0
- package/dist/state/id-map.d.mts +41 -0
- package/dist/state/id-map.d.mts.map +1 -0
- package/dist/state/id-map.mjs +61 -0
- package/dist/state/id-map.mjs.map +1 -0
- package/dist/state/import.cjs +268 -0
- package/dist/state/import.cjs.map +1 -0
- package/dist/state/import.d.cts +29 -0
- package/dist/state/import.d.cts.map +1 -0
- package/dist/state/import.d.mts +29 -0
- package/dist/state/import.d.mts.map +1 -0
- package/dist/state/import.mjs +264 -0
- package/dist/state/import.mjs.map +1 -0
- package/dist/state/payload.cjs +169 -0
- package/dist/state/payload.cjs.map +1 -0
- package/dist/state/payload.d.cts +288 -0
- package/dist/state/payload.d.cts.map +1 -0
- package/dist/state/payload.d.mts +288 -0
- package/dist/state/payload.d.mts.map +1 -0
- package/dist/state/payload.mjs +162 -0
- package/dist/state/payload.mjs.map +1 -0
- package/dist/state/snapshot.cjs +202 -0
- package/dist/state/snapshot.cjs.map +1 -0
- package/dist/state/snapshot.d.cts +120 -0
- package/dist/state/snapshot.d.cts.map +1 -0
- package/dist/state/snapshot.d.mts +120 -0
- package/dist/state/snapshot.d.mts.map +1 -0
- package/dist/state/snapshot.mjs +198 -0
- package/dist/state/snapshot.mjs.map +1 -0
- package/dist/state/utils.cjs +68 -0
- package/dist/state/utils.cjs.map +1 -0
- package/dist/state/utils.d.cts +49 -0
- package/dist/state/utils.d.cts.map +1 -0
- package/dist/state/utils.d.mts +49 -0
- package/dist/state/utils.d.mts.map +1 -0
- package/dist/state/utils.mjs +61 -0
- package/dist/state/utils.mjs.map +1 -0
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +3 -3
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +3 -3
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export.mjs","sourceRoot":"","sources":["../../src/state/export.ts"],"names":[],"mappings":"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","sourcesContent":["import { AccountWalletType } from '@metamask/account-api';\nimport { HdKeyring } from '@metamask/eth-hd-keyring/v2';\nimport { EthAccountType } from '@metamask/keyring-api';\nimport { PrivateKeyExportedAccount } from '@metamask/keyring-api/v2';\nimport { KeyringTypes } from '@metamask/keyring-controller';\n\nimport type {\n AccountTreeControllerMessenger,\n AccountTreeControllerState,\n} from '../types.js';\nimport type { AccountWalletObject } from '../wallet.js';\nimport {\n AccountWalletEntropyObject,\n AccountWalletKeyringObject,\n} from '../wallet.js';\nimport { IdMap } from './id-map.js';\nimport type {\n AccountTreeWalletEntry,\n AccountWalletMnemonicGroupEntry,\n AccountWalletMnemonicPayload,\n AccountWalletPrivateKeyGroupEntry,\n AccountWalletPrivateKeyPayload,\n ExportStateOptions,\n} from './payload.js';\nimport {\n AccountWalletPayloadType,\n AccountWalletPrivateKeyEncoding,\n toWalletPayloadId,\n toGroupPayloadId,\n} from './payload.js';\nimport { AccountTreeSnapshot } from './snapshot.js';\nimport { encodeBytes } from './utils.js';\n\n/**\n * Returns `true` if `wallet` is an HD entropy wallet ({@link AccountWalletEntropyObject}).\n *\n * @param wallet - The wallet object to test.\n * @returns Type predicate narrowing to {@link AccountWalletEntropyObject}.\n */\nexport function isMnemonicWalletObject(\n wallet: AccountWalletObject,\n): wallet is AccountWalletEntropyObject {\n return wallet.type === AccountWalletType.Entropy;\n}\n\n/**\n * Returns `true` if `wallet` is an imported private-key wallet\n * ({@link AccountWalletKeyringObject} with keyring type {@link KeyringTypes.simple}).\n *\n * @param wallet - The wallet object to test.\n * @returns Type predicate narrowing to {@link AccountWalletKeyringObject}.\n */\nexport function isPrivateKeyWalletObject(\n wallet: AccountWalletObject,\n): wallet is AccountWalletKeyringObject {\n return (\n wallet.type === AccountWalletType.Keyring &&\n wallet.metadata.keyring.type === KeyringTypes.simple\n );\n}\n\n/** Context required by {@link exportState}. */\nexport type ExportContext = {\n getState: () => AccountTreeControllerState;\n messenger: AccountTreeControllerMessenger;\n};\n\n/**\n * Exports a single entropy (HD) wallet object as an {@link AccountWalletMnemonicPayload}.\n *\n * Calls `KeyringController:withKeyringV2Unsafe` to derive the stable entropy source ID\n * via {@link HdKeyring.toEntropySourceId} and, when `includeSecrets` is `true`, to read\n * the raw mnemonic bytes.\n *\n * @param context - Export context.\n * @param walletObj - The local entropy wallet to export.\n * @param includeSecrets - When `true`, the BIP-39 mnemonic is included in the payload.\n * @param idMap - ID map to populate with local↔payload ID pairs for this wallet and its groups.\n * @returns The mnemonic wallet payload entry.\n * @throws If `includeSecrets` is `true` but the mnemonic cannot be read from the keyring.\n */\nasync function exportMnemonicWalletObject(\n context: ExportContext,\n walletObj: AccountWalletEntropyObject,\n includeSecrets: boolean,\n idMap: IdMap,\n): Promise<AccountWalletMnemonicPayload> {\n const result = await context.messenger.call(\n 'KeyringController:withKeyringV2Unsafe',\n // The local wallet entropy ID is the keyring ID.\n { id: walletObj.metadata.entropy.id },\n async ({ keyring }) => {\n const hdKeyring = keyring as HdKeyring;\n const includeMnemonic =\n includeSecrets &&\n hdKeyring.mnemonic !== null &&\n hdKeyring.mnemonic !== undefined;\n\n return {\n // Compute the stable entropy source ID from the keyring's mnemonic (BIP-39 seed).\n entropySourceId: await hdKeyring.toEntropySourceId(),\n // No need to include the mnemonic here if we're not exporting secrets.\n mnemonic: includeMnemonic ? hdKeyring.mnemonic : undefined,\n };\n },\n );\n const { entropySourceId, mnemonic } = result as {\n entropySourceId: string;\n mnemonic?: Uint8Array;\n };\n\n // We use the stable entropy source ID as the payload wallet ID, rather than the local wallet ID, to\n // ensure that the exported snapshot is stable across different installations and sessions.\n const wallet: AccountWalletMnemonicPayload = {\n type: AccountWalletPayloadType.Mnemonic,\n id: toWalletPayloadId(entropySourceId),\n metadata: { name: walletObj.metadata.name },\n groups: [],\n };\n\n idMap.add(walletObj.id, wallet.id);\n\n for (const groupObj of Object.values(walletObj.groups)) {\n const { groupIndex } = groupObj.metadata.entropy;\n\n const group: AccountWalletMnemonicGroupEntry = {\n id: toGroupPayloadId(wallet.id, groupIndex),\n groupIndex,\n metadata: {\n name: groupObj.metadata.name,\n pinned: groupObj.metadata.pinned,\n hidden: groupObj.metadata.hidden,\n },\n };\n\n idMap.add(groupObj.id, group.id);\n\n wallet.groups.push(group);\n }\n\n // Sort the groups by their `groupIndex` to ensure a stable order in the exported payload.\n wallet.groups.sort(\n (group, otherGroup) => group.groupIndex - otherGroup.groupIndex,\n );\n\n // This should never happen, but we check just in case.\n if (includeSecrets) {\n if (mnemonic === undefined) {\n throw new Error(`Failed to export mnemonic for wallet ${wallet.id}`);\n }\n\n wallet.value = encodeBytes(mnemonic);\n }\n\n return wallet;\n}\n\n/**\n * Exports a single simple-keyring wallet object as an {@link AccountWalletPrivateKeyPayload}.\n *\n * All groups from the wallet are merged into the `'private-key'` singleton payload entry.\n * When `includeSecrets` is `true`, each group's private key is exported via\n * `KeyringController:withKeyringV2`.\n *\n * @param context - Export context.\n * @param walletObj - The local simple-keyring wallet to export.\n * @param includeSecrets - When `true`, private keys are included in the payload.\n * @param idMap - ID map to populate with local↔payload ID pairs for this wallet and its groups.\n * @returns The private-key wallet payload entry.\n * @throws If `includeSecrets` is `true` but a private key cannot be exported for an account.\n */\nasync function exportPrivateKeyWalletObject(\n context: ExportContext,\n walletObj: AccountWalletKeyringObject,\n includeSecrets: boolean,\n idMap: IdMap,\n): Promise<AccountWalletPrivateKeyPayload> {\n // We use a singleton wallet ID for private keys.\n const wallet: AccountWalletPrivateKeyPayload = {\n type: AccountWalletPayloadType.PrivateKey,\n id: toWalletPayloadId(AccountWalletPayloadType.PrivateKey),\n metadata: { name: walletObj.metadata.name },\n groups: [],\n };\n\n for (const groupObj of Object.values(walletObj.groups)) {\n const accountId = groupObj.accounts[0];\n if (!accountId) {\n continue;\n }\n const account = context.messenger.call(\n 'AccountsController:getAccount',\n accountId,\n );\n if (!account) {\n continue;\n }\n\n const { address } = account;\n\n let exported: PrivateKeyExportedAccount | undefined;\n if (includeSecrets) {\n const result = await context.messenger.call(\n 'KeyringController:withKeyringV2',\n { address },\n async ({ keyring }) => {\n if (!keyring.exportAccount) {\n throw new Error(\n `Keyring for account ${accountId} does not support exportAccount`,\n );\n }\n\n return keyring.exportAccount(accountId, {\n type: 'private-key',\n encoding: AccountWalletPrivateKeyEncoding.Hexadecimal,\n });\n },\n );\n\n exported = result as PrivateKeyExportedAccount;\n }\n\n const group: AccountWalletPrivateKeyGroupEntry = {\n id: toGroupPayloadId(wallet.id, address),\n metadata: {\n name: groupObj.metadata.name,\n pinned: groupObj.metadata.pinned,\n hidden: groupObj.metadata.hidden,\n },\n };\n\n if (includeSecrets) {\n if (!exported) {\n throw new Error(\n `Failed to export private key for account ${accountId}`,\n );\n }\n group.value = {\n privateKey: encodeBytes(new TextEncoder().encode(exported.privateKey)),\n encoding: exported.encoding,\n type: EthAccountType.Eoa,\n };\n }\n\n idMap.add(groupObj.id, group.id);\n\n wallet.groups.push(group);\n }\n\n return wallet;\n}\n\n/**\n * Builds an {@link AccountTreeSnapshot} from the current controller state.\n *\n * Iterates over all wallets in the tree:\n * - {@link AccountWalletType.Entropy} (HD) wallets -> `'mnemonic'` payload entries.\n * - {@link AccountWalletType.Keyring} wallets of type `simple` -> `'private-key'` payload entries.\n * - Snap wallets and hardware keyrings are skipped in v1.\n *\n * @param context - Export context providing state and messenger access.\n * @param options - Export options.\n * @returns A promise that resolves to the built snapshot.\n * @throws If `options.includeSecrets` is `true` and the vault is locked.\n */\nexport async function exportState(\n context: ExportContext,\n options: ExportStateOptions = {},\n): Promise<AccountTreeSnapshot> {\n const state = context.getState();\n\n const includeSecrets = options.includeSecrets ?? false;\n const { isUnlocked } = context.messenger.call('KeyringController:getState');\n if (includeSecrets && !isUnlocked) {\n throw new Error('Cannot include secrets in export when vault is locked');\n }\n\n const idMap = new IdMap();\n const entries: AccountTreeWalletEntry[] = [];\n let privateKeyWallet: AccountWalletPrivateKeyPayload | undefined;\n\n for (const walletObj of Object.values(state.accountTree.wallets)) {\n if (isMnemonicWalletObject(walletObj)) {\n entries.push(\n await exportMnemonicWalletObject(\n context,\n walletObj,\n includeSecrets,\n idMap,\n ),\n );\n } else if (isPrivateKeyWalletObject(walletObj)) {\n const exported = await exportPrivateKeyWalletObject(\n context,\n walletObj,\n includeSecrets,\n idMap,\n );\n\n if (privateKeyWallet) {\n privateKeyWallet.groups.push(...exported.groups);\n } else {\n privateKeyWallet = exported;\n\n // Register only once, since all private keys are exported into the same wallet entry.\n idMap.add(walletObj.id, exported.id);\n }\n } else {\n // AccountWalletType.Snap and hardware keyrings: skipped for now.\n }\n }\n\n if (privateKeyWallet) {\n entries.push(privateKeyWallet);\n }\n\n return new AccountTreeSnapshot(entries, idMap);\n}\n"]}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
5
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
6
|
+
};
|
|
7
|
+
var _IdMap_localToPayload, _IdMap_payloadToLocal;
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.IdMap = void 0;
|
|
10
|
+
const utils_1 = require("@metamask/utils");
|
|
11
|
+
/**
|
|
12
|
+
* Bidirectional map between local controller IDs and portable payload IDs.
|
|
13
|
+
*
|
|
14
|
+
* Populated during {@link exportState} so that callers can bridge between
|
|
15
|
+
* device-local wallet/group IDs and the stable cross-device IDs that appear
|
|
16
|
+
* in a serialized {@link AccountTreePayload}.
|
|
17
|
+
*/
|
|
18
|
+
class IdMap {
|
|
19
|
+
/**
|
|
20
|
+
* @param entries - Optional seed pairs `[localId, payloadId]` to pre-populate the map.
|
|
21
|
+
*/
|
|
22
|
+
constructor(entries = []) {
|
|
23
|
+
_IdMap_localToPayload.set(this, new Map());
|
|
24
|
+
_IdMap_payloadToLocal.set(this, new Map());
|
|
25
|
+
for (const [localId, payloadId] of entries) {
|
|
26
|
+
this.add(localId, payloadId);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Registers a local↔payload ID pair.
|
|
31
|
+
*
|
|
32
|
+
* @param localId - Local controller wallet or group ID.
|
|
33
|
+
* @param payloadId - Corresponding portable payload ID.
|
|
34
|
+
*/
|
|
35
|
+
add(localId, payloadId) {
|
|
36
|
+
// Each local ID maps to exactly one payload ID and vice versa: wallet and
|
|
37
|
+
// group IDs are unique by construction, so a duplicate signals a bug in the
|
|
38
|
+
// caller (e.g. exportState visiting the same node twice).
|
|
39
|
+
(0, utils_1.assert)(!__classPrivateFieldGet(this, _IdMap_localToPayload, "f").has(localId), `Local ID already registered: ${localId}`);
|
|
40
|
+
(0, utils_1.assert)(!__classPrivateFieldGet(this, _IdMap_payloadToLocal, "f").has(payloadId), `Payload ID already registered: ${payloadId}`);
|
|
41
|
+
__classPrivateFieldGet(this, _IdMap_localToPayload, "f").set(localId, payloadId);
|
|
42
|
+
__classPrivateFieldGet(this, _IdMap_payloadToLocal, "f").set(payloadId, localId);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Returns the portable payload ID for a given local ID.
|
|
46
|
+
*
|
|
47
|
+
* @param localId - Local controller wallet or group ID.
|
|
48
|
+
* @returns The payload ID, or `undefined` if not registered.
|
|
49
|
+
*/
|
|
50
|
+
getPayloadId(localId) {
|
|
51
|
+
return __classPrivateFieldGet(this, _IdMap_localToPayload, "f").get(localId);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Returns the local controller ID for a given payload ID.
|
|
55
|
+
*
|
|
56
|
+
* @param payloadId - Portable payload ID.
|
|
57
|
+
* @returns The local wallet or group ID, or `undefined` if not registered.
|
|
58
|
+
*/
|
|
59
|
+
getLocalId(payloadId) {
|
|
60
|
+
return __classPrivateFieldGet(this, _IdMap_payloadToLocal, "f").get(payloadId);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.IdMap = IdMap;
|
|
64
|
+
_IdMap_localToPayload = new WeakMap(), _IdMap_payloadToLocal = new WeakMap();
|
|
65
|
+
//# sourceMappingURL=id-map.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id-map.cjs","sourceRoot":"","sources":["../../src/state/id-map.ts"],"names":[],"mappings":";;;;;;;;;AACA,2CAAyC;AAUzC;;;;;;GAMG;AACH,MAAa,KAAK;IAKhB;;OAEG;IACH,YAAY,UAAsD,EAAE;QAP3D,gCAA2C,IAAI,GAAG,EAAE,EAAC;QAErD,gCAA2C,IAAI,GAAG,EAAE,EAAC;QAM5D,KAAK,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,IAAI,OAAO,EAAE,CAAC;YAC3C,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,GAAG,CAAC,OAAgB,EAAE,SAAoB;QACxC,0EAA0E;QAC1E,4EAA4E;QAC5E,0DAA0D;QAC1D,IAAA,cAAM,EACJ,CAAC,uBAAA,IAAI,6BAAgB,CAAC,GAAG,CAAC,OAAO,CAAC,EAClC,gCAAgC,OAAO,EAAE,CAC1C,CAAC;QACF,IAAA,cAAM,EACJ,CAAC,uBAAA,IAAI,6BAAgB,CAAC,GAAG,CAAC,SAAS,CAAC,EACpC,kCAAkC,SAAS,EAAE,CAC9C,CAAC;QACF,uBAAA,IAAI,6BAAgB,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAC7C,uBAAA,IAAI,6BAAgB,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;IAED;;;;;OAKG;IACH,YAAY,CAAC,OAAgB;QAC3B,OAAO,uBAAA,IAAI,6BAAgB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED;;;;;OAKG;IACH,UAAU,CAAC,SAAoB;QAC7B,OAAO,uBAAA,IAAI,6BAAgB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC7C,CAAC;CACF;AAvDD,sBAuDC","sourcesContent":["import type { AccountGroupId, AccountWalletId } from '@metamask/account-api';\nimport { assert } from '@metamask/utils';\n\nimport type {\n AccountGroupPayloadId,\n AccountWalletPayloadId,\n} from './payload.js';\n\ntype LocalId = AccountWalletId | AccountGroupId;\ntype PayloadId = AccountWalletPayloadId | AccountGroupPayloadId;\n\n/**\n * Bidirectional map between local controller IDs and portable payload IDs.\n *\n * Populated during {@link exportState} so that callers can bridge between\n * device-local wallet/group IDs and the stable cross-device IDs that appear\n * in a serialized {@link AccountTreePayload}.\n */\nexport class IdMap {\n readonly #localToPayload: Map<LocalId, PayloadId> = new Map();\n\n readonly #payloadToLocal: Map<PayloadId, LocalId> = new Map();\n\n /**\n * @param entries - Optional seed pairs `[localId, payloadId]` to pre-populate the map.\n */\n constructor(entries: [localId: LocalId, payloadId: PayloadId][] = []) {\n for (const [localId, payloadId] of entries) {\n this.add(localId, payloadId);\n }\n }\n\n /**\n * Registers a local↔payload ID pair.\n *\n * @param localId - Local controller wallet or group ID.\n * @param payloadId - Corresponding portable payload ID.\n */\n add(localId: LocalId, payloadId: PayloadId): void {\n // Each local ID maps to exactly one payload ID and vice versa: wallet and\n // group IDs are unique by construction, so a duplicate signals a bug in the\n // caller (e.g. exportState visiting the same node twice).\n assert(\n !this.#localToPayload.has(localId),\n `Local ID already registered: ${localId}`,\n );\n assert(\n !this.#payloadToLocal.has(payloadId),\n `Payload ID already registered: ${payloadId}`,\n );\n this.#localToPayload.set(localId, payloadId);\n this.#payloadToLocal.set(payloadId, localId);\n }\n\n /**\n * Returns the portable payload ID for a given local ID.\n *\n * @param localId - Local controller wallet or group ID.\n * @returns The payload ID, or `undefined` if not registered.\n */\n getPayloadId(localId: LocalId): PayloadId | undefined {\n return this.#localToPayload.get(localId);\n }\n\n /**\n * Returns the local controller ID for a given payload ID.\n *\n * @param payloadId - Portable payload ID.\n * @returns The local wallet or group ID, or `undefined` if not registered.\n */\n getLocalId(payloadId: PayloadId): LocalId | undefined {\n return this.#payloadToLocal.get(payloadId);\n }\n}\n"]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { AccountGroupId, AccountWalletId } from "@metamask/account-api";
|
|
2
|
+
import type { AccountGroupPayloadId, AccountWalletPayloadId } from "./payload.cjs";
|
|
3
|
+
type LocalId = AccountWalletId | AccountGroupId;
|
|
4
|
+
type PayloadId = AccountWalletPayloadId | AccountGroupPayloadId;
|
|
5
|
+
/**
|
|
6
|
+
* Bidirectional map between local controller IDs and portable payload IDs.
|
|
7
|
+
*
|
|
8
|
+
* Populated during {@link exportState} so that callers can bridge between
|
|
9
|
+
* device-local wallet/group IDs and the stable cross-device IDs that appear
|
|
10
|
+
* in a serialized {@link AccountTreePayload}.
|
|
11
|
+
*/
|
|
12
|
+
export declare class IdMap {
|
|
13
|
+
#private;
|
|
14
|
+
/**
|
|
15
|
+
* @param entries - Optional seed pairs `[localId, payloadId]` to pre-populate the map.
|
|
16
|
+
*/
|
|
17
|
+
constructor(entries?: [localId: LocalId, payloadId: PayloadId][]);
|
|
18
|
+
/**
|
|
19
|
+
* Registers a local↔payload ID pair.
|
|
20
|
+
*
|
|
21
|
+
* @param localId - Local controller wallet or group ID.
|
|
22
|
+
* @param payloadId - Corresponding portable payload ID.
|
|
23
|
+
*/
|
|
24
|
+
add(localId: LocalId, payloadId: PayloadId): void;
|
|
25
|
+
/**
|
|
26
|
+
* Returns the portable payload ID for a given local ID.
|
|
27
|
+
*
|
|
28
|
+
* @param localId - Local controller wallet or group ID.
|
|
29
|
+
* @returns The payload ID, or `undefined` if not registered.
|
|
30
|
+
*/
|
|
31
|
+
getPayloadId(localId: LocalId): PayloadId | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Returns the local controller ID for a given payload ID.
|
|
34
|
+
*
|
|
35
|
+
* @param payloadId - Portable payload ID.
|
|
36
|
+
* @returns The local wallet or group ID, or `undefined` if not registered.
|
|
37
|
+
*/
|
|
38
|
+
getLocalId(payloadId: PayloadId): LocalId | undefined;
|
|
39
|
+
}
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=id-map.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id-map.d.cts","sourceRoot":"","sources":["../../src/state/id-map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,8BAA8B;AAG7E,OAAO,KAAK,EACV,qBAAqB,EACrB,sBAAsB,EACvB,sBAAqB;AAEtB,KAAK,OAAO,GAAG,eAAe,GAAG,cAAc,CAAC;AAChD,KAAK,SAAS,GAAG,sBAAsB,GAAG,qBAAqB,CAAC;AAEhE;;;;;;GAMG;AACH,qBAAa,KAAK;;IAKhB;;OAEG;gBACS,OAAO,GAAE,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC,EAAO;IAMpE;;;;;OAKG;IACH,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI;IAgBjD;;;;;OAKG;IACH,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS;IAIrD;;;;;OAKG;IACH,UAAU,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS;CAGtD"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { AccountGroupId, AccountWalletId } from "@metamask/account-api";
|
|
2
|
+
import type { AccountGroupPayloadId, AccountWalletPayloadId } from "./payload.mjs";
|
|
3
|
+
type LocalId = AccountWalletId | AccountGroupId;
|
|
4
|
+
type PayloadId = AccountWalletPayloadId | AccountGroupPayloadId;
|
|
5
|
+
/**
|
|
6
|
+
* Bidirectional map between local controller IDs and portable payload IDs.
|
|
7
|
+
*
|
|
8
|
+
* Populated during {@link exportState} so that callers can bridge between
|
|
9
|
+
* device-local wallet/group IDs and the stable cross-device IDs that appear
|
|
10
|
+
* in a serialized {@link AccountTreePayload}.
|
|
11
|
+
*/
|
|
12
|
+
export declare class IdMap {
|
|
13
|
+
#private;
|
|
14
|
+
/**
|
|
15
|
+
* @param entries - Optional seed pairs `[localId, payloadId]` to pre-populate the map.
|
|
16
|
+
*/
|
|
17
|
+
constructor(entries?: [localId: LocalId, payloadId: PayloadId][]);
|
|
18
|
+
/**
|
|
19
|
+
* Registers a local↔payload ID pair.
|
|
20
|
+
*
|
|
21
|
+
* @param localId - Local controller wallet or group ID.
|
|
22
|
+
* @param payloadId - Corresponding portable payload ID.
|
|
23
|
+
*/
|
|
24
|
+
add(localId: LocalId, payloadId: PayloadId): void;
|
|
25
|
+
/**
|
|
26
|
+
* Returns the portable payload ID for a given local ID.
|
|
27
|
+
*
|
|
28
|
+
* @param localId - Local controller wallet or group ID.
|
|
29
|
+
* @returns The payload ID, or `undefined` if not registered.
|
|
30
|
+
*/
|
|
31
|
+
getPayloadId(localId: LocalId): PayloadId | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Returns the local controller ID for a given payload ID.
|
|
34
|
+
*
|
|
35
|
+
* @param payloadId - Portable payload ID.
|
|
36
|
+
* @returns The local wallet or group ID, or `undefined` if not registered.
|
|
37
|
+
*/
|
|
38
|
+
getLocalId(payloadId: PayloadId): LocalId | undefined;
|
|
39
|
+
}
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=id-map.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id-map.d.mts","sourceRoot":"","sources":["../../src/state/id-map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,8BAA8B;AAG7E,OAAO,KAAK,EACV,qBAAqB,EACrB,sBAAsB,EACvB,sBAAqB;AAEtB,KAAK,OAAO,GAAG,eAAe,GAAG,cAAc,CAAC;AAChD,KAAK,SAAS,GAAG,sBAAsB,GAAG,qBAAqB,CAAC;AAEhE;;;;;;GAMG;AACH,qBAAa,KAAK;;IAKhB;;OAEG;gBACS,OAAO,GAAE,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC,EAAO;IAMpE;;;;;OAKG;IACH,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI;IAgBjD;;;;;OAKG;IACH,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS;IAIrD;;;;;OAKG;IACH,UAAU,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS;CAGtD"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
2
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
+
};
|
|
6
|
+
var _IdMap_localToPayload, _IdMap_payloadToLocal;
|
|
7
|
+
import { assert } from "@metamask/utils";
|
|
8
|
+
/**
|
|
9
|
+
* Bidirectional map between local controller IDs and portable payload IDs.
|
|
10
|
+
*
|
|
11
|
+
* Populated during {@link exportState} so that callers can bridge between
|
|
12
|
+
* device-local wallet/group IDs and the stable cross-device IDs that appear
|
|
13
|
+
* in a serialized {@link AccountTreePayload}.
|
|
14
|
+
*/
|
|
15
|
+
export class IdMap {
|
|
16
|
+
/**
|
|
17
|
+
* @param entries - Optional seed pairs `[localId, payloadId]` to pre-populate the map.
|
|
18
|
+
*/
|
|
19
|
+
constructor(entries = []) {
|
|
20
|
+
_IdMap_localToPayload.set(this, new Map());
|
|
21
|
+
_IdMap_payloadToLocal.set(this, new Map());
|
|
22
|
+
for (const [localId, payloadId] of entries) {
|
|
23
|
+
this.add(localId, payloadId);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Registers a local↔payload ID pair.
|
|
28
|
+
*
|
|
29
|
+
* @param localId - Local controller wallet or group ID.
|
|
30
|
+
* @param payloadId - Corresponding portable payload ID.
|
|
31
|
+
*/
|
|
32
|
+
add(localId, payloadId) {
|
|
33
|
+
// Each local ID maps to exactly one payload ID and vice versa: wallet and
|
|
34
|
+
// group IDs are unique by construction, so a duplicate signals a bug in the
|
|
35
|
+
// caller (e.g. exportState visiting the same node twice).
|
|
36
|
+
assert(!__classPrivateFieldGet(this, _IdMap_localToPayload, "f").has(localId), `Local ID already registered: ${localId}`);
|
|
37
|
+
assert(!__classPrivateFieldGet(this, _IdMap_payloadToLocal, "f").has(payloadId), `Payload ID already registered: ${payloadId}`);
|
|
38
|
+
__classPrivateFieldGet(this, _IdMap_localToPayload, "f").set(localId, payloadId);
|
|
39
|
+
__classPrivateFieldGet(this, _IdMap_payloadToLocal, "f").set(payloadId, localId);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Returns the portable payload ID for a given local ID.
|
|
43
|
+
*
|
|
44
|
+
* @param localId - Local controller wallet or group ID.
|
|
45
|
+
* @returns The payload ID, or `undefined` if not registered.
|
|
46
|
+
*/
|
|
47
|
+
getPayloadId(localId) {
|
|
48
|
+
return __classPrivateFieldGet(this, _IdMap_localToPayload, "f").get(localId);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Returns the local controller ID for a given payload ID.
|
|
52
|
+
*
|
|
53
|
+
* @param payloadId - Portable payload ID.
|
|
54
|
+
* @returns The local wallet or group ID, or `undefined` if not registered.
|
|
55
|
+
*/
|
|
56
|
+
getLocalId(payloadId) {
|
|
57
|
+
return __classPrivateFieldGet(this, _IdMap_payloadToLocal, "f").get(payloadId);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
_IdMap_localToPayload = new WeakMap(), _IdMap_payloadToLocal = new WeakMap();
|
|
61
|
+
//# sourceMappingURL=id-map.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id-map.mjs","sourceRoot":"","sources":["../../src/state/id-map.ts"],"names":[],"mappings":";;;;;;AACA,OAAO,EAAE,MAAM,EAAE,wBAAwB;AAUzC;;;;;;GAMG;AACH,MAAM,OAAO,KAAK;IAKhB;;OAEG;IACH,YAAY,UAAsD,EAAE;QAP3D,gCAA2C,IAAI,GAAG,EAAE,EAAC;QAErD,gCAA2C,IAAI,GAAG,EAAE,EAAC;QAM5D,KAAK,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,IAAI,OAAO,EAAE,CAAC;YAC3C,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,GAAG,CAAC,OAAgB,EAAE,SAAoB;QACxC,0EAA0E;QAC1E,4EAA4E;QAC5E,0DAA0D;QAC1D,MAAM,CACJ,CAAC,uBAAA,IAAI,6BAAgB,CAAC,GAAG,CAAC,OAAO,CAAC,EAClC,gCAAgC,OAAO,EAAE,CAC1C,CAAC;QACF,MAAM,CACJ,CAAC,uBAAA,IAAI,6BAAgB,CAAC,GAAG,CAAC,SAAS,CAAC,EACpC,kCAAkC,SAAS,EAAE,CAC9C,CAAC;QACF,uBAAA,IAAI,6BAAgB,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAC7C,uBAAA,IAAI,6BAAgB,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;IAED;;;;;OAKG;IACH,YAAY,CAAC,OAAgB;QAC3B,OAAO,uBAAA,IAAI,6BAAgB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED;;;;;OAKG;IACH,UAAU,CAAC,SAAoB;QAC7B,OAAO,uBAAA,IAAI,6BAAgB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC7C,CAAC;CACF","sourcesContent":["import type { AccountGroupId, AccountWalletId } from '@metamask/account-api';\nimport { assert } from '@metamask/utils';\n\nimport type {\n AccountGroupPayloadId,\n AccountWalletPayloadId,\n} from './payload.js';\n\ntype LocalId = AccountWalletId | AccountGroupId;\ntype PayloadId = AccountWalletPayloadId | AccountGroupPayloadId;\n\n/**\n * Bidirectional map between local controller IDs and portable payload IDs.\n *\n * Populated during {@link exportState} so that callers can bridge between\n * device-local wallet/group IDs and the stable cross-device IDs that appear\n * in a serialized {@link AccountTreePayload}.\n */\nexport class IdMap {\n readonly #localToPayload: Map<LocalId, PayloadId> = new Map();\n\n readonly #payloadToLocal: Map<PayloadId, LocalId> = new Map();\n\n /**\n * @param entries - Optional seed pairs `[localId, payloadId]` to pre-populate the map.\n */\n constructor(entries: [localId: LocalId, payloadId: PayloadId][] = []) {\n for (const [localId, payloadId] of entries) {\n this.add(localId, payloadId);\n }\n }\n\n /**\n * Registers a local↔payload ID pair.\n *\n * @param localId - Local controller wallet or group ID.\n * @param payloadId - Corresponding portable payload ID.\n */\n add(localId: LocalId, payloadId: PayloadId): void {\n // Each local ID maps to exactly one payload ID and vice versa: wallet and\n // group IDs are unique by construction, so a duplicate signals a bug in the\n // caller (e.g. exportState visiting the same node twice).\n assert(\n !this.#localToPayload.has(localId),\n `Local ID already registered: ${localId}`,\n );\n assert(\n !this.#payloadToLocal.has(payloadId),\n `Payload ID already registered: ${payloadId}`,\n );\n this.#localToPayload.set(localId, payloadId);\n this.#payloadToLocal.set(payloadId, localId);\n }\n\n /**\n * Returns the portable payload ID for a given local ID.\n *\n * @param localId - Local controller wallet or group ID.\n * @returns The payload ID, or `undefined` if not registered.\n */\n getPayloadId(localId: LocalId): PayloadId | undefined {\n return this.#localToPayload.get(localId);\n }\n\n /**\n * Returns the local controller ID for a given payload ID.\n *\n * @param payloadId - Portable payload ID.\n * @returns The local wallet or group ID, or `undefined` if not registered.\n */\n getLocalId(payloadId: PayloadId): LocalId | undefined {\n return this.#payloadToLocal.get(payloadId);\n }\n}\n"]}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.importState = void 0;
|
|
4
|
+
const account_api_1 = require("@metamask/account-api");
|
|
5
|
+
const accounts_controller_1 = require("@metamask/accounts-controller");
|
|
6
|
+
const v2_1 = require("@metamask/eth-hd-keyring/v2");
|
|
7
|
+
const keyring_api_1 = require("@metamask/keyring-api");
|
|
8
|
+
const keyring_controller_1 = require("@metamask/keyring-controller");
|
|
9
|
+
const export_js_1 = require("./export.cjs");
|
|
10
|
+
const payload_js_1 = require("./payload.cjs");
|
|
11
|
+
const utils_js_1 = require("./utils.cjs");
|
|
12
|
+
/**
|
|
13
|
+
* Searches the local wallet tree for an entropy wallet whose stable payload ID
|
|
14
|
+
* matches `payloadWalletId`. The entropy source ID is derived on-the-fly via
|
|
15
|
+
* `KeyringController:withKeyringV2Unsafe` rather than relying on cached metadata.
|
|
16
|
+
*
|
|
17
|
+
* @param context - Import context.
|
|
18
|
+
* @param payloadWalletId - Payload wallet ID to match against.
|
|
19
|
+
* @returns The matching local entropy wallet, or `undefined` if not found.
|
|
20
|
+
*/
|
|
21
|
+
async function findLocalWalletMnemonicFromPayloadId(context, payloadWalletId) {
|
|
22
|
+
const wallets = Object.values(context.getState().accountTree.wallets);
|
|
23
|
+
for (const wallet of wallets) {
|
|
24
|
+
if ((0, export_js_1.isMnemonicWalletObject)(wallet)) {
|
|
25
|
+
const result = await context.messenger.call('KeyringController:withKeyringV2Unsafe', { id: wallet.metadata.entropy.id }, async ({ keyring }) => {
|
|
26
|
+
const hdKeyring = keyring;
|
|
27
|
+
return (0, payload_js_1.toWalletPayloadId)(await hdKeyring.toEntropySourceId());
|
|
28
|
+
});
|
|
29
|
+
const localPayloadId = result;
|
|
30
|
+
if (localPayloadId === payloadWalletId) {
|
|
31
|
+
return wallet;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Returns the local entropy wallet for the given ID.
|
|
39
|
+
*
|
|
40
|
+
* @param context - Import context.
|
|
41
|
+
* @param id - Local wallet ID.
|
|
42
|
+
* @returns The entropy wallet object.
|
|
43
|
+
* @throws If the wallet is not found or is not an entropy wallet.
|
|
44
|
+
*/
|
|
45
|
+
function findLocalWalletMnemonicFromId(context, id) {
|
|
46
|
+
const localWallets = context.getState().accountTree.wallets;
|
|
47
|
+
const localWallet = localWallets[id];
|
|
48
|
+
if (!localWallet) {
|
|
49
|
+
throw new Error(`Failed to import mnemonic wallet: wallet not found after creation`);
|
|
50
|
+
}
|
|
51
|
+
if (!(0, export_js_1.isMnemonicWalletObject)(localWallet)) {
|
|
52
|
+
throw new Error(`Failed to import mnemonic wallet: wallet is not of type 'mnemonic'`);
|
|
53
|
+
}
|
|
54
|
+
return localWallet;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Returns the local simple-keyring wallet, or `undefined` if none exists yet.
|
|
58
|
+
*
|
|
59
|
+
* The private-key wallet ID is static (derived solely from {@link KeyringTypes.simple}),
|
|
60
|
+
* so no runtime argument is needed to locate it.
|
|
61
|
+
*
|
|
62
|
+
* @param context - Import context.
|
|
63
|
+
* @returns The keyring wallet object, or `undefined` if not present in state.
|
|
64
|
+
*/
|
|
65
|
+
function findLocalWalletPrivateKey(context) {
|
|
66
|
+
const localWalletId = (0, account_api_1.toAccountWalletId)(account_api_1.AccountWalletType.Keyring, keyring_controller_1.KeyringTypes.simple);
|
|
67
|
+
const localWallet = context.getState().accountTree.wallets[localWalletId];
|
|
68
|
+
return localWallet?.type === account_api_1.AccountWalletType.Keyring
|
|
69
|
+
? localWallet
|
|
70
|
+
: undefined;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Returns `true` if the given payload group is already present in the local simple-keyring wallet.
|
|
74
|
+
*
|
|
75
|
+
* @param localWallet - The local simple-keyring wallet, or `undefined` if none exists yet.
|
|
76
|
+
* @param group - The payload group entry to check.
|
|
77
|
+
* @returns Whether the group's account is already tracked locally.
|
|
78
|
+
*/
|
|
79
|
+
function hasLocalGroupPrivateKey(localWallet, group) {
|
|
80
|
+
const address = (0, payload_js_1.parsePayloadGroupId)(group.id).subId;
|
|
81
|
+
const localWalletId = (0, account_api_1.toAccountWalletId)(account_api_1.AccountWalletType.Keyring, keyring_controller_1.KeyringTypes.simple);
|
|
82
|
+
const localGroupId = (0, account_api_1.toAccountGroupId)(localWalletId, address);
|
|
83
|
+
const localGroup = localWallet?.groups[localGroupId];
|
|
84
|
+
const accountId = (0, accounts_controller_1.getUUIDFromAddressOfNormalAccount)(address);
|
|
85
|
+
return localGroup?.accounts.some((id) => id === accountId) ?? false;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Applies name, pinned, and hidden metadata from a payload group entry to a local group.
|
|
89
|
+
*
|
|
90
|
+
* @param context - Import context providing the metadata setters.
|
|
91
|
+
* @param localGroupId - Local group ID to update.
|
|
92
|
+
* @param payloadGroupMetadata - Metadata from the payload group entry.
|
|
93
|
+
*/
|
|
94
|
+
function setGroupMetadata(context, localGroupId, payloadGroupMetadata) {
|
|
95
|
+
context.setAccountGroupName(localGroupId, payloadGroupMetadata.name);
|
|
96
|
+
context.setAccountGroupPinned(localGroupId, payloadGroupMetadata.pinned);
|
|
97
|
+
context.setAccountGroupHideen(localGroupId, payloadGroupMetadata.hidden);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Computes the contiguous ranges of group indices that are present in the payload
|
|
101
|
+
* but absent from the local wallet, so they can be created in batches.
|
|
102
|
+
*
|
|
103
|
+
* @param localWallet - The local entropy wallet to check existing groups against.
|
|
104
|
+
* @param payloadGroups - Ordered group entries from the payload.
|
|
105
|
+
* @returns An array of `[fromGroupIndex, toGroupIndex]` ranges to create.
|
|
106
|
+
*/
|
|
107
|
+
function getRangesFromPayloadGroups(localWallet, payloadGroups) {
|
|
108
|
+
let rangeIndex;
|
|
109
|
+
const ranges = [];
|
|
110
|
+
// Keep track of the last payload group so we can close the final range if needed.
|
|
111
|
+
let lastPayloadGroup;
|
|
112
|
+
for (const payloadGroup of payloadGroups) {
|
|
113
|
+
const localGroupId = (0, account_api_1.toMultichainAccountGroupId)(localWallet.id, payloadGroup.groupIndex);
|
|
114
|
+
if (localWallet.groups[localGroupId]) {
|
|
115
|
+
if (rangeIndex !== undefined) {
|
|
116
|
+
ranges.push([rangeIndex, payloadGroup.groupIndex - 1]);
|
|
117
|
+
rangeIndex = undefined;
|
|
118
|
+
}
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
rangeIndex ?? (rangeIndex = payloadGroup.groupIndex);
|
|
122
|
+
lastPayloadGroup = payloadGroup;
|
|
123
|
+
}
|
|
124
|
+
if (rangeIndex !== undefined && lastPayloadGroup !== undefined) {
|
|
125
|
+
ranges.push([rangeIndex, lastPayloadGroup.groupIndex]);
|
|
126
|
+
}
|
|
127
|
+
return ranges;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Applies a mnemonic wallet payload entry to the local state.
|
|
131
|
+
*
|
|
132
|
+
* If no local wallet with the same entropy source ID exists and a mnemonic is
|
|
133
|
+
* present in the payload, a new HD wallet is created via
|
|
134
|
+
* `MultichainAccountService:createMultichainAccountWallet`. Missing groups are
|
|
135
|
+
* created in batches via `MultichainAccountService:createMultichainAccountGroups`.
|
|
136
|
+
* Metadata (name, pinned, hidden) is applied to all groups afterward.
|
|
137
|
+
*
|
|
138
|
+
* @param context - Import context.
|
|
139
|
+
* @param payloadWallet - The mnemonic wallet entry from the payload.
|
|
140
|
+
*/
|
|
141
|
+
async function importMnemonicWallet(context, payloadWallet) {
|
|
142
|
+
// Find the local wallet with the same entropy source ID if it exists.
|
|
143
|
+
let localWallet = await findLocalWalletMnemonicFromPayloadId(context, payloadWallet.id);
|
|
144
|
+
if (!localWallet) {
|
|
145
|
+
if (!payloadWallet.value) {
|
|
146
|
+
// No mnemonic in payload and wallet doesn't exist locally -- nothing to do.
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
// Import the mnemonic as a new HD wallet.
|
|
150
|
+
const mnemonic = (0, utils_js_1.decodeBytes)(payloadWallet.value);
|
|
151
|
+
const { id } = await context.messenger.call('MultichainAccountService:createMultichainAccountWallet', { type: 'import', mnemonic });
|
|
152
|
+
// Event handlers fire synchronously, so the wallet is in the tree now.
|
|
153
|
+
localWallet = findLocalWalletMnemonicFromId(context, id);
|
|
154
|
+
}
|
|
155
|
+
context.setWalletName(localWallet.id, payloadWallet.metadata.name);
|
|
156
|
+
for (const range of getRangesFromPayloadGroups(localWallet, payloadWallet.groups)) {
|
|
157
|
+
await context.messenger.call('MultichainAccountService:createMultichainAccountGroups', {
|
|
158
|
+
entropySource: localWallet.metadata.entropy.id,
|
|
159
|
+
fromGroupIndex: range[0],
|
|
160
|
+
toGroupIndex: range[1],
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
// Re-read wallet after groups creation.
|
|
164
|
+
localWallet = findLocalWalletMnemonicFromId(context, localWallet.id);
|
|
165
|
+
for (const payloadGroup of payloadWallet.groups) {
|
|
166
|
+
const localGroupId = (0, account_api_1.toMultichainAccountGroupId)(localWallet.id, payloadGroup.groupIndex);
|
|
167
|
+
setGroupMetadata(context, localGroupId, payloadGroup.metadata);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Applies private-key wallet group entries from the payload to the local state.
|
|
172
|
+
*
|
|
173
|
+
* For each group the account address is derived from the payload group ID. If the
|
|
174
|
+
* account does not yet exist locally and a private key is provided, it is imported
|
|
175
|
+
* via `KeyringController:withKeyringV2` using the `'private-key:import'` constructor.
|
|
176
|
+
* Metadata (name, pinned, hidden) is then applied to the local group.
|
|
177
|
+
*
|
|
178
|
+
* @param context - Import context.
|
|
179
|
+
* @param payloadGroups - Private-key group entries from the payload.
|
|
180
|
+
*/
|
|
181
|
+
async function importPrivateKeyWallet(context, payloadGroups) {
|
|
182
|
+
const localWalletId = (0, account_api_1.toAccountWalletId)(account_api_1.AccountWalletType.Keyring, keyring_controller_1.KeyringTypes.simple);
|
|
183
|
+
// Only EVM EOA accounts are supported for now. Non-EVM private keys require
|
|
184
|
+
// Snap-based import routing (ADR-0007), which is not yet implemented. Skip the
|
|
185
|
+
// entire entry so payloads from future clients are accepted without crashing.
|
|
186
|
+
// In the same pass, collect groups whose key is not yet present locally.
|
|
187
|
+
const localWallet = findLocalWalletPrivateKey(context);
|
|
188
|
+
const supportedGroups = [];
|
|
189
|
+
const missingGroups = [];
|
|
190
|
+
for (const group of payloadGroups) {
|
|
191
|
+
const type = group.value?.type;
|
|
192
|
+
if (type !== undefined && type !== keyring_api_1.EthAccountType.Eoa) {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
supportedGroups.push(group);
|
|
196
|
+
if (!hasLocalGroupPrivateKey(localWallet, group) &&
|
|
197
|
+
group.value !== undefined) {
|
|
198
|
+
missingGroups.push(group);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Import all missing keys in a single :withController call to avoid
|
|
202
|
+
// triggering multiple `KeyringController:stateChanged` events.
|
|
203
|
+
if (missingGroups.length > 0) {
|
|
204
|
+
await context.messenger.call('KeyringController:withController', async (controller) => {
|
|
205
|
+
// Find the existing simple keyring or create one if this is the
|
|
206
|
+
// first private-key import (e.g. during onboarding).
|
|
207
|
+
const existing = controller.keyrings.find(({ keyring }) => keyring.type === keyring_controller_1.KeyringTypes.simple);
|
|
208
|
+
const { keyringV2 } = existing ?? (await controller.addNewKeyring(keyring_controller_1.KeyringTypes.simple));
|
|
209
|
+
if (!keyringV2) {
|
|
210
|
+
throw new Error('Simple keyring has no v2 interface');
|
|
211
|
+
}
|
|
212
|
+
for (const group of missingGroups) {
|
|
213
|
+
// Safe to assert `group.value` is present because we filtered out groups
|
|
214
|
+
// without a `value` above.
|
|
215
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
216
|
+
const { privateKey: privateKeyBytes, encoding } = group.value;
|
|
217
|
+
const privateKey = new TextDecoder().decode((0, utils_js_1.decodeBytes)(privateKeyBytes));
|
|
218
|
+
const [account] = await keyringV2.createAccounts({
|
|
219
|
+
type: 'private-key:import',
|
|
220
|
+
accountType: keyring_api_1.EthAccountType.Eoa,
|
|
221
|
+
privateKey,
|
|
222
|
+
encoding,
|
|
223
|
+
});
|
|
224
|
+
if (!account) {
|
|
225
|
+
throw new Error('Failed to import private key for account');
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
// Re-read state once after all imports, then apply metadata to every group
|
|
231
|
+
// that now exists locally.
|
|
232
|
+
for (const group of supportedGroups) {
|
|
233
|
+
const address = (0, payload_js_1.parsePayloadGroupId)(group.id).subId;
|
|
234
|
+
const localGroupId = (0, account_api_1.toAccountGroupId)(localWalletId, address);
|
|
235
|
+
const localGroup = findLocalWalletPrivateKey(context)?.groups[localGroupId];
|
|
236
|
+
if (!localGroup) {
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
setGroupMetadata(context, localGroup.id, group.metadata);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Applies an {@link AccountTreeSnapshot} to the current controller state.
|
|
244
|
+
*
|
|
245
|
+
* The snapshot must already have been validated — typically via
|
|
246
|
+
* {@link AccountTreeSnapshot.deserialize}. For each retained wallet:
|
|
247
|
+
*
|
|
248
|
+
* - `'mnemonic'`: imports the mnemonic when provided and not already present,
|
|
249
|
+
* then applies metadata to all groups.
|
|
250
|
+
* - `'private-key'`: imports each retained group's key when provided and not
|
|
251
|
+
* already present, then applies metadata.
|
|
252
|
+
*
|
|
253
|
+
* @param context - Import context providing state, messenger, and setters.
|
|
254
|
+
* @param snapshot - The validated snapshot to import.
|
|
255
|
+
*/
|
|
256
|
+
async function importState(context, snapshot) {
|
|
257
|
+
const payload = snapshot.serialize();
|
|
258
|
+
for (const wallet of payload.wallets) {
|
|
259
|
+
if (wallet.type === payload_js_1.AccountWalletPayloadType.Mnemonic) {
|
|
260
|
+
await importMnemonicWallet(context, wallet);
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
await importPrivateKeyWallet(context, wallet.groups);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
exports.importState = importState;
|
|
268
|
+
//# sourceMappingURL=import.cjs.map
|
|
@@ -0,0 +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 setAccountGroupHideen: (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.setAccountGroupHideen(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 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 for (const payloadGroup of payloadWallet.groups) {\n const localGroupId = toMultichainAccountGroupId(\n localWallet.id,\n payloadGroup.groupIndex,\n );\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"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { AccountGroupId, AccountWalletId } from "@metamask/account-api";
|
|
2
|
+
import type { AccountTreeControllerMessenger, AccountTreeControllerState } from "../types.cjs";
|
|
3
|
+
import type { AccountTreeSnapshot } from "./snapshot.cjs";
|
|
4
|
+
/** Context required by {@link importState}. */
|
|
5
|
+
export type ImportContext = {
|
|
6
|
+
getState: () => AccountTreeControllerState;
|
|
7
|
+
messenger: AccountTreeControllerMessenger;
|
|
8
|
+
setWalletName: (walletId: AccountWalletId, name: string) => void;
|
|
9
|
+
/** Sets a group name. Implementations must resolve name conflicts automatically. */
|
|
10
|
+
setAccountGroupName: (groupId: AccountGroupId, name: string) => void;
|
|
11
|
+
setAccountGroupPinned: (groupId: AccountGroupId, pinned: boolean) => void;
|
|
12
|
+
setAccountGroupHideen: (groupId: AccountGroupId, hidden: boolean) => void;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Applies an {@link AccountTreeSnapshot} to the current controller state.
|
|
16
|
+
*
|
|
17
|
+
* The snapshot must already have been validated — typically via
|
|
18
|
+
* {@link AccountTreeSnapshot.deserialize}. For each retained wallet:
|
|
19
|
+
*
|
|
20
|
+
* - `'mnemonic'`: imports the mnemonic when provided and not already present,
|
|
21
|
+
* then applies metadata to all groups.
|
|
22
|
+
* - `'private-key'`: imports each retained group's key when provided and not
|
|
23
|
+
* already present, then applies metadata.
|
|
24
|
+
*
|
|
25
|
+
* @param context - Import context providing state, messenger, and setters.
|
|
26
|
+
* @param snapshot - The validated snapshot to import.
|
|
27
|
+
*/
|
|
28
|
+
export declare function importState(context: ImportContext, snapshot: AccountTreeSnapshot): Promise<void>;
|
|
29
|
+
//# sourceMappingURL=import.d.cts.map
|
|
@@ -0,0 +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;AAmVF;;;;;;;;;;;;;GAaG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,mBAAmB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAUf"}
|