@metamask/accounts-controller 31.0.0 → 32.0.1
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 +26 -1
- package/dist/AccountsController.cjs +125 -134
- package/dist/AccountsController.cjs.map +1 -1
- package/dist/AccountsController.d.cts.map +1 -1
- package/dist/AccountsController.d.mts.map +1 -1
- package/dist/AccountsController.mjs +128 -136
- package/dist/AccountsController.mjs.map +1 -1
- package/dist/tests/mocks.cjs +12 -2
- package/dist/tests/mocks.cjs.map +1 -1
- package/dist/tests/mocks.d.cts +13 -3
- package/dist/tests/mocks.d.cts.map +1 -1
- package/dist/tests/mocks.d.mts +13 -3
- package/dist/tests/mocks.d.mts.map +1 -1
- package/dist/tests/mocks.mjs +13 -3
- package/dist/tests/mocks.mjs.map +1 -1
- package/dist/utils.cjs +83 -5
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +59 -4
- package/dist/utils.d.cts.map +1 -1
- package/dist/utils.d.mts +59 -4
- package/dist/utils.d.mts.map +1 -1
- package/dist/utils.mjs +77 -3
- package/dist/utils.mjs.map +1 -1
- package/package.json +15 -12
@@ -3,14 +3,16 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
3
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
4
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
5
5
|
};
|
6
|
-
var _AccountsController_instances, _AccountsController_assertAccountCanBeRenamed,
|
6
|
+
var _AccountsController_instances, _AccountsController_assertAccountCanBeRenamed, _AccountsController_getInternalAccountForNonSnapAccount, _AccountsController_getSnapKeyring, _AccountsController_handleOnSnapKeyringAccountEvent, _AccountsController_handleOnKeyringStateChange, _AccountsController_update, _AccountsController_handleOnSnapStateChange, _AccountsController_getAccountsByKeyringType, _AccountsController_getLastSelectedAccount, _AccountsController_getLastSelectedIndex, _AccountsController_getInternalAccountFromAddressAndType, _AccountsController_handleOnMultichainNetworkDidChange, _AccountsController_subscribeToMessageEvents, _AccountsController_registerMessageHandlers;
|
7
7
|
import { BaseController } from "@metamask/base-controller";
|
8
8
|
import { SnapKeyring } from "@metamask/eth-snap-keyring";
|
9
|
-
import { EthAccountType, EthMethod, EthScope, isEvmAccountType } from "@metamask/keyring-api";
|
9
|
+
import { EthAccountType, EthMethod, EthScope, isEvmAccountType, KeyringAccountEntropyTypeOption } from "@metamask/keyring-api";
|
10
10
|
import { KeyringTypes } from "@metamask/keyring-controller";
|
11
11
|
import { isScopeEqualToAny } from "@metamask/keyring-utils";
|
12
12
|
import { isCaipChainId } from "@metamask/utils";
|
13
|
-
import
|
13
|
+
import $lodash from "lodash";
|
14
|
+
const { cloneDeep } = $lodash;
|
15
|
+
import { getEvmDerivationPathForIndex, getEvmGroupIndexFromAddressIndex, getUUIDFromAddressOfNormalAccount, isHdKeyringType, isHdSnapKeyringAccount, isSimpleKeyringType, isSnapKeyringType, keyringTypeToName } from "./utils.mjs";
|
14
16
|
const controllerName = 'AccountsController';
|
15
17
|
const accountsControllerMetadata = {
|
16
18
|
internalAccounts: {
|
@@ -266,38 +268,41 @@ export class AccountsController extends BaseController {
|
|
266
268
|
* @returns A Promise that resolves when the accounts have been updated.
|
267
269
|
*/
|
268
270
|
async updateAccounts() {
|
269
|
-
const
|
270
|
-
const
|
271
|
-
|
272
|
-
const
|
273
|
-
const
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
271
|
+
const keyringAccountIndexes = new Map();
|
272
|
+
const existingInternalAccounts = this.state.internalAccounts.accounts;
|
273
|
+
const internalAccounts = {};
|
274
|
+
const { keyrings } = this.messagingSystem.call('KeyringController:getState');
|
275
|
+
for (const keyring of keyrings) {
|
276
|
+
const keyringTypeName = keyringTypeToName(keyring.type);
|
277
|
+
for (const address of keyring.accounts) {
|
278
|
+
const internalAccount = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getInternalAccountFromAddressAndType).call(this, address, keyring);
|
279
|
+
// This should never really happen, but if for some reason we're not
|
280
|
+
// able to get the Snap keyring reference, this would return an
|
281
|
+
// undefined account.
|
282
|
+
// So we just skip it, even though, this should not really happen.
|
283
|
+
if (!internalAccount) {
|
284
|
+
continue;
|
285
|
+
}
|
286
|
+
// Get current index for this keyring (we use human indexing, so start at 1).
|
287
|
+
const keyringAccountIndex = keyringAccountIndexes.get(keyringTypeName) ?? 1;
|
288
|
+
const existingAccount = existingInternalAccounts[internalAccount.id];
|
289
|
+
internalAccounts[internalAccount.id] = {
|
290
|
+
...internalAccount,
|
291
|
+
metadata: {
|
292
|
+
...internalAccount.metadata,
|
293
|
+
// Re-use existing metadata if any.
|
294
|
+
name: existingAccount?.metadata.name ??
|
295
|
+
`${keyringTypeName} ${keyringAccountIndex}`,
|
296
|
+
importTime: existingAccount?.metadata.importTime ?? Date.now(),
|
297
|
+
lastSelected: existingAccount?.metadata.lastSelected ?? 0,
|
298
|
+
},
|
299
|
+
};
|
300
|
+
// Increment the account index for this keyring.
|
301
|
+
keyringAccountIndexes.set(keyringTypeName, keyringAccountIndex + 1);
|
285
302
|
}
|
286
|
-
|
287
|
-
internalAccountMap[internalAccount.id] = {
|
288
|
-
...internalAccount,
|
289
|
-
metadata: {
|
290
|
-
...internalAccount.metadata,
|
291
|
-
name: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, existingAccount?.id, 'name') ??
|
292
|
-
`${keyringTypeName} ${keyringAccountIndex + 1}`,
|
293
|
-
importTime: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, existingAccount?.id, 'importTime') ?? Date.now(),
|
294
|
-
lastSelected: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, existingAccount?.id, 'lastSelected') ?? 0,
|
295
|
-
},
|
296
|
-
};
|
297
|
-
return internalAccountMap;
|
298
|
-
}, {});
|
303
|
+
}
|
299
304
|
__classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_update).call(this, (state) => {
|
300
|
-
state.internalAccounts.accounts =
|
305
|
+
state.internalAccounts.accounts = internalAccounts;
|
301
306
|
});
|
302
307
|
}
|
303
308
|
/**
|
@@ -345,11 +350,63 @@ _AccountsController_instances = new WeakSet(), _AccountsController_assertAccount
|
|
345
350
|
internalAccount.id !== account.id)) {
|
346
351
|
throw new Error('Account name already exists');
|
347
352
|
}
|
348
|
-
},
|
353
|
+
}, _AccountsController_getInternalAccountForNonSnapAccount = function _AccountsController_getInternalAccountForNonSnapAccount(address, keyring) {
|
354
|
+
const id = getUUIDFromAddressOfNormalAccount(address);
|
355
|
+
// We might have an account for this ID already, so we'll just re-use
|
356
|
+
// the same metadata
|
357
|
+
const account = this.getAccount(id);
|
358
|
+
const metadata = {
|
359
|
+
name: account?.metadata.name ?? '',
|
360
|
+
...(account?.metadata.nameLastUpdatedAt
|
361
|
+
? {
|
362
|
+
nameLastUpdatedAt: account?.metadata.nameLastUpdatedAt,
|
363
|
+
}
|
364
|
+
: {}),
|
365
|
+
importTime: account?.metadata.importTime ?? Date.now(),
|
366
|
+
lastSelected: account?.metadata.lastSelected ?? 0,
|
367
|
+
keyring: {
|
368
|
+
type: keyring.type,
|
369
|
+
},
|
370
|
+
};
|
371
|
+
let options = {};
|
372
|
+
if (isHdKeyringType(keyring.type)) {
|
373
|
+
// We need to find the account index from its HD keyring.
|
374
|
+
const groupIndex = getEvmGroupIndexFromAddressIndex(keyring, address);
|
375
|
+
// If for some reason, we cannot find this address, then the caller made a mistake
|
376
|
+
// and it did not use the proper keyring object. For now, we do not fail and just
|
377
|
+
// consider this account as "simple account".
|
378
|
+
if (groupIndex !== undefined) {
|
379
|
+
// NOTE: We are not using the `hdPath` from the associated keyring here and
|
380
|
+
// getting the keyring instance here feels a bit overkill.
|
381
|
+
// This will be naturally fixed once every keyring start using `KeyringAccount` and implement the keyring API.
|
382
|
+
const derivationPath = getEvmDerivationPathForIndex(groupIndex);
|
383
|
+
// Those are "legacy options" and they were used before `KeyringAccount` added
|
384
|
+
// support for type options. We keep those temporarily until we update everything
|
385
|
+
// to use the new typed options.
|
386
|
+
const legacyOptions = {
|
387
|
+
entropySource: keyring.metadata.id,
|
388
|
+
derivationPath,
|
389
|
+
groupIndex,
|
390
|
+
};
|
391
|
+
// New typed entropy options. This is required for multichain accounts.
|
392
|
+
const entropyOptions = {
|
393
|
+
entropy: {
|
394
|
+
type: KeyringAccountEntropyTypeOption.Mnemonic,
|
395
|
+
id: keyring.metadata.id,
|
396
|
+
derivationPath,
|
397
|
+
groupIndex,
|
398
|
+
},
|
399
|
+
};
|
400
|
+
options = {
|
401
|
+
...legacyOptions,
|
402
|
+
...entropyOptions,
|
403
|
+
};
|
404
|
+
}
|
405
|
+
}
|
349
406
|
return {
|
350
|
-
id
|
407
|
+
id,
|
351
408
|
address,
|
352
|
-
options
|
409
|
+
options,
|
353
410
|
methods: [
|
354
411
|
EthMethod.PersonalSign,
|
355
412
|
EthMethod.Sign,
|
@@ -360,88 +417,13 @@ _AccountsController_instances = new WeakSet(), _AccountsController_assertAccount
|
|
360
417
|
],
|
361
418
|
scopes: [EthScope.Eoa],
|
362
419
|
type: EthAccountType.Eoa,
|
363
|
-
metadata
|
364
|
-
name: '',
|
365
|
-
importTime: Date.now(),
|
366
|
-
keyring: {
|
367
|
-
type,
|
368
|
-
},
|
369
|
-
},
|
420
|
+
metadata,
|
370
421
|
};
|
371
422
|
}, _AccountsController_getSnapKeyring = function _AccountsController_getSnapKeyring() {
|
372
423
|
const [snapKeyring] = this.messagingSystem.call('KeyringController:getKeyringsByType', SnapKeyring.type);
|
373
424
|
// Snap keyring is not available until the first account is created in the keyring
|
374
425
|
// controller, so this might be undefined.
|
375
426
|
return snapKeyring;
|
376
|
-
}, _AccountsController_listSnapAccounts =
|
377
|
-
/**
|
378
|
-
* Returns a list of internal accounts created using the SnapKeyring.
|
379
|
-
*
|
380
|
-
* @returns A promise that resolves to an array of InternalAccount objects.
|
381
|
-
*/
|
382
|
-
async function _AccountsController_listSnapAccounts() {
|
383
|
-
const keyring = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getSnapKeyring).call(this);
|
384
|
-
if (!keyring) {
|
385
|
-
return [];
|
386
|
-
}
|
387
|
-
return keyring.listAccounts();
|
388
|
-
}, _AccountsController_listNormalAccounts =
|
389
|
-
/**
|
390
|
-
* Returns a list of normal accounts.
|
391
|
-
* Note: listNormalAccounts is a temporary method until the keyrings all implement the InternalAccount interface.
|
392
|
-
* Once all keyrings implement the InternalAccount interface, this method can be removed and getAccounts can be used instead.
|
393
|
-
*
|
394
|
-
* @returns A Promise that resolves to an array of InternalAccount objects.
|
395
|
-
*/
|
396
|
-
async function _AccountsController_listNormalAccounts() {
|
397
|
-
const internalAccounts = [];
|
398
|
-
const { keyrings } = this.messagingSystem.call('KeyringController:getState');
|
399
|
-
for (const keyring of keyrings) {
|
400
|
-
const keyringType = keyring.type;
|
401
|
-
if (!isNormalKeyringType(keyringType)) {
|
402
|
-
// We only consider "normal accounts" here, so keep looping
|
403
|
-
continue;
|
404
|
-
}
|
405
|
-
for (const [accountIndex, address] of keyring.accounts.entries()) {
|
406
|
-
const id = getUUIDFromAddressOfNormalAccount(address);
|
407
|
-
let options = {};
|
408
|
-
if (isHdKeyringType(keyring.type)) {
|
409
|
-
options = {
|
410
|
-
entropySource: keyring.metadata.id,
|
411
|
-
// NOTE: We are not using the `hdPath` from the associated keyring here and
|
412
|
-
// getting the keyring instance here feels a bit overkill.
|
413
|
-
// This will be naturally fixed once every keyring start using `KeyringAccount` and implement the keyring API.
|
414
|
-
derivationPath: getDerivationPathForIndex(accountIndex),
|
415
|
-
};
|
416
|
-
}
|
417
|
-
const nameLastUpdatedAt = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, id, 'nameLastUpdatedAt');
|
418
|
-
internalAccounts.push({
|
419
|
-
id,
|
420
|
-
address,
|
421
|
-
options,
|
422
|
-
methods: [
|
423
|
-
EthMethod.PersonalSign,
|
424
|
-
EthMethod.Sign,
|
425
|
-
EthMethod.SignTransaction,
|
426
|
-
EthMethod.SignTypedDataV1,
|
427
|
-
EthMethod.SignTypedDataV3,
|
428
|
-
EthMethod.SignTypedDataV4,
|
429
|
-
],
|
430
|
-
scopes: [EthScope.Eoa],
|
431
|
-
type: EthAccountType.Eoa,
|
432
|
-
metadata: {
|
433
|
-
name: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, id, 'name') ?? '',
|
434
|
-
...(nameLastUpdatedAt && { nameLastUpdatedAt }),
|
435
|
-
importTime: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, id, 'importTime') ?? Date.now(),
|
436
|
-
lastSelected: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, id, 'lastSelected') ?? 0,
|
437
|
-
keyring: {
|
438
|
-
type: keyringType,
|
439
|
-
},
|
440
|
-
},
|
441
|
-
});
|
442
|
-
}
|
443
|
-
}
|
444
|
-
return internalAccounts;
|
445
427
|
}, _AccountsController_handleOnSnapKeyringAccountEvent = function _AccountsController_handleOnSnapKeyringAccountEvent(event, ...payload) {
|
446
428
|
this.messagingSystem.publish(event, ...payload);
|
447
429
|
}, _AccountsController_handleOnKeyringStateChange = function _AccountsController_handleOnKeyringStateChange({ isUnlocked, keyrings, }) {
|
@@ -468,7 +450,7 @@ async function _AccountsController_listNormalAccounts() {
|
|
468
450
|
// Gets the patch object based on the keyring type (since Snap accounts and other accounts
|
469
451
|
// are handled differently).
|
470
452
|
const patchOf = (type) => {
|
471
|
-
if (type
|
453
|
+
if (isSnapKeyringType(type)) {
|
472
454
|
return patches.snap;
|
473
455
|
}
|
474
456
|
return patches.normal;
|
@@ -495,11 +477,7 @@ async function _AccountsController_listNormalAccounts() {
|
|
495
477
|
// Otherwise, that's a new account.
|
496
478
|
patch.added.push({
|
497
479
|
address,
|
498
|
-
|
499
|
-
// Automatically injects `entropySource` for HD accounts only.
|
500
|
-
options: keyring.type === KeyringTypes.hd
|
501
|
-
? { entropySource: keyring.metadata.id }
|
502
|
-
: {},
|
480
|
+
keyring,
|
503
481
|
});
|
504
482
|
}
|
505
483
|
// Keep track of those address to check for removed accounts later.
|
@@ -529,7 +507,7 @@ async function _AccountsController_listNormalAccounts() {
|
|
529
507
|
diff.removed.push(account.id);
|
530
508
|
}
|
531
509
|
for (const added of patch.added) {
|
532
|
-
const account = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getInternalAccountFromAddressAndType).call(this, added.address, added.
|
510
|
+
const account = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getInternalAccountFromAddressAndType).call(this, added.address, added.keyring);
|
533
511
|
if (account) {
|
534
512
|
// Re-compute the list of accounts everytime, so we can make sure new names
|
535
513
|
// are also considered.
|
@@ -546,10 +524,6 @@ async function _AccountsController_listNormalAccounts() {
|
|
546
524
|
importTime: Date.now(),
|
547
525
|
lastSelected,
|
548
526
|
},
|
549
|
-
options: {
|
550
|
-
...account.options,
|
551
|
-
...added.options,
|
552
|
-
},
|
553
527
|
};
|
554
528
|
diff.added.push(internalAccounts.accounts[account.id]);
|
555
529
|
}
|
@@ -634,10 +608,9 @@ async function _AccountsController_listNormalAccounts() {
|
|
634
608
|
return (accounts ?? this.listMultichainAccounts()).filter((internalAccount) => {
|
635
609
|
// We do consider `hd` and `simple` keyrings to be of same type. So we check those 2 types
|
636
610
|
// to group those accounts together!
|
637
|
-
if (keyringType
|
638
|
-
|
639
|
-
|
640
|
-
internalAccount.metadata.keyring.type === KeyringTypes.simple);
|
611
|
+
if (isHdKeyringType(keyringType) || isSimpleKeyringType(keyringType)) {
|
612
|
+
return (isHdKeyringType(internalAccount.metadata.keyring.type) ||
|
613
|
+
isSimpleKeyringType(internalAccount.metadata.keyring.type));
|
641
614
|
}
|
642
615
|
return internalAccount.metadata.keyring.type === keyringType;
|
643
616
|
});
|
@@ -652,18 +625,40 @@ async function _AccountsController_listNormalAccounts() {
|
|
652
625
|
// NOTE: For now we use the current date, since we know this value
|
653
626
|
// will always be higher than any already selected account index.
|
654
627
|
return Date.now();
|
655
|
-
}, _AccountsController_getInternalAccountFromAddressAndType = function _AccountsController_getInternalAccountFromAddressAndType(address,
|
656
|
-
if (type
|
657
|
-
const
|
628
|
+
}, _AccountsController_getInternalAccountFromAddressAndType = function _AccountsController_getInternalAccountFromAddressAndType(address, keyring) {
|
629
|
+
if (isSnapKeyringType(keyring.type)) {
|
630
|
+
const snapKeyring = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getSnapKeyring).call(this);
|
658
631
|
// We need the Snap keyring to retrieve the account from its address.
|
659
|
-
if (!
|
632
|
+
if (!snapKeyring) {
|
660
633
|
return undefined;
|
661
634
|
}
|
662
635
|
// This might be undefined if the Snap deleted the account before
|
663
636
|
// reaching that point.
|
664
|
-
|
637
|
+
let account = snapKeyring.getAccountByAddress(address);
|
638
|
+
if (account) {
|
639
|
+
// We force the copy here, to avoid mutating the reference returned by the Snap keyring.
|
640
|
+
account = cloneDeep(account);
|
641
|
+
// MIGRATION: To avoid any existing Snap account migration, we are
|
642
|
+
// just "adding" the new typed options that we need for multichain
|
643
|
+
// accounts. Ultimately, we would need a real Snap account migrations
|
644
|
+
// (being handled by each Snaps).
|
645
|
+
if (isHdSnapKeyringAccount(account)) {
|
646
|
+
const options = {
|
647
|
+
...account.options,
|
648
|
+
entropy: {
|
649
|
+
type: KeyringAccountEntropyTypeOption.Mnemonic,
|
650
|
+
id: account.options.entropySource,
|
651
|
+
groupIndex: account.options.index,
|
652
|
+
derivationPath: account.options.derivationPath,
|
653
|
+
},
|
654
|
+
};
|
655
|
+
// Inject the new typed options to the internal account copy.
|
656
|
+
account.options = options;
|
657
|
+
}
|
658
|
+
}
|
659
|
+
return account;
|
665
660
|
}
|
666
|
-
return __classPrivateFieldGet(this, _AccountsController_instances, "m",
|
661
|
+
return __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getInternalAccountForNonSnapAccount).call(this, address, keyring);
|
667
662
|
}, _AccountsController_handleOnMultichainNetworkDidChange = function _AccountsController_handleOnMultichainNetworkDidChange(id) {
|
668
663
|
let accountId;
|
669
664
|
// We only support non-EVM Caip chain IDs at the moment. Ex Solana and Bitcoin
|
@@ -685,9 +680,6 @@ async function _AccountsController_listNormalAccounts() {
|
|
685
680
|
currentState.internalAccounts.selectedAccount = accountId;
|
686
681
|
});
|
687
682
|
// DO NOT publish AccountsController:setSelectedAccount to prevent circular listener loops
|
688
|
-
}, _AccountsController_populateExistingMetadata = function _AccountsController_populateExistingMetadata(accountId, metadataKey, account) {
|
689
|
-
const internalAccount = account ?? this.getAccount(accountId);
|
690
|
-
return internalAccount ? internalAccount.metadata[metadataKey] : undefined;
|
691
683
|
}, _AccountsController_subscribeToMessageEvents = function _AccountsController_subscribeToMessageEvents() {
|
692
684
|
this.messagingSystem.subscribe('SnapController:stateChange', (snapStateState) => __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_handleOnSnapStateChange).call(this, snapStateState));
|
693
685
|
this.messagingSystem.subscribe('KeyringController:stateChange', (keyringState) => __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_handleOnKeyringStateChange).call(this, keyringState));
|