Skip to content
Open
Show file tree
Hide file tree
Changes from 98 commits
Commits
Show all changes
115 commits
Select commit Hold shift + click to select a range
aa318fe
feat: have group align method print all provider errors
hmalik88 Sep 14, 2025
17e855f
Merge branch 'main' into hm/bip-44-perf-and-devx-improvements
hmalik88 Sep 15, 2025
924a679
Merge branch 'main' into hm/bip-44-perf-and-devx-improvements
hmalik88 Sep 17, 2025
be8b2f7
Merge branch 'main' into hm/bip-44-perf-and-devx-improvements
hmalik88 Sep 17, 2025
b49f7b4
feat: construct service state at the top level and pass state slices …
hmalik88 Sep 18, 2025
a261e1e
Merge branch 'main' into hm/bip-44-perf-and-devx-improvements
hmalik88 Sep 18, 2025
6104131
chore: readd code removed by mistake
hmalik88 Sep 18, 2025
c45f612
feat: add getAccounts method to AccountsController
hmalik88 Sep 18, 2025
acf36e0
chore: update types to include getAccounts action
hmalik88 Sep 18, 2025
ce3cef4
refactor: derive account ID and use that to do a getAccounts call ins…
hmalik88 Sep 18, 2025
891fa50
feat: finish refactor
hmalik88 Sep 18, 2025
83060d6
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Sep 18, 2025
01096bf
chore: add JSDoc for wallet init
hmalik88 Sep 18, 2025
4c2e582
chore: remove accountId to context mapping since with the removal of …
hmalik88 Sep 19, 2025
43972f8
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Sep 19, 2025
7cec854
feat: start to add logic that will let createMultichainAccountWallet …
hmalik88 Sep 22, 2025
f05e6da
feat: remove need for mnemonicAsBytes
hmalik88 Sep 22, 2025
e325c77
Merge branch 'main' into hm/bip-44-perf-and-devx-improvements
hmalik88 Sep 26, 2025
6c5b86e
feat: update creatMultichainAccountWallet method to cover all entry p…
hmalik88 Sep 26, 2025
7d805ef
chore: update JSDoc
hmalik88 Sep 26, 2025
6b6e776
chore: update log messages
hmalik88 Sep 26, 2025
33e5e34
chore: add more JSDocs
hmalik88 Sep 26, 2025
f4d79c0
test: update multichain service tests
hmalik88 Sep 26, 2025
350aa93
fix: address type errors, remove try/catch block to not swallow error…
hmalik88 Sep 26, 2025
ce322e0
fix: lint fixes
hmalik88 Sep 26, 2025
31a6d71
chore: remove commented test
hmalik88 Sep 26, 2025
4c485a6
refactor: make changes to messenger and providers to make the compati…
hmalik88 Sep 26, 2025
c82de17
refactor: simplify init and properly filter for rejected already alig…
hmalik88 Sep 26, 2025
7d6e76a
fix: properly initialize group state
hmalik88 Sep 26, 2025
2d55dbd
test: update multichain account group tests
hmalik88 Sep 26, 2025
b8a87ff
test: update multichain account wallet tests
hmalik88 Sep 26, 2025
92d1c3a
test: update provider tests so that they are compatible with the new …
hmalik88 Sep 26, 2025
8dedd1a
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Sep 26, 2025
11cc131
test: update btc and trx tests to account for new logic
hmalik88 Sep 26, 2025
d28a324
chore: update changelogs
hmalik88 Sep 27, 2025
f308e9f
fix: lint fixes
hmalik88 Sep 27, 2025
8bc1237
test: add test for getAccounts
hmalik88 Sep 27, 2025
19cfdf3
feat: register getAccounts message handler
hmalik88 Sep 27, 2025
82fece8
chore: update JSDoc
hmalik88 Sep 27, 2025
c812a82
refactor: use Object.entries in wallet init
hmalik88 Sep 27, 2025
91aa52e
refactor: simplified error logic and include provider names in error …
hmalik88 Sep 27, 2025
4b19ed5
test: have account group creation test check actual failure message
hmalik88 Sep 27, 2025
5bcaa53
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Oct 9, 2025
c12435a
test: update tests
hmalik88 Oct 9, 2025
e9ab58d
chore: remove old comment
hmalik88 Oct 9, 2025
521a71b
chore: update changelog
hmalik88 Oct 9, 2025
1162348
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Oct 13, 2025
31f0193
feat: add code to remove disabled provider state from groups
hmalik88 Oct 13, 2025
7128b55
test: add tests for disabled provider
hmalik88 Oct 13, 2025
0c9324c
fix: update param passed to clean disabled provider state function
hmalik88 Oct 13, 2025
6b15c2d
fix: update logic and tests
hmalik88 Oct 13, 2025
0ec9181
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Oct 13, 2025
98db5d3
fix: lint fix
hmalik88 Oct 13, 2025
98d6373
refactor: rename function and add JSDoc
hmalik88 Oct 13, 2025
5733fac
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Oct 17, 2025
eab65ca
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Oct 17, 2025
99d70dd
chore: apply code review
hmalik88 Oct 17, 2025
6f02157
fix: lint fix
hmalik88 Oct 19, 2025
a544fb3
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Oct 23, 2025
b7a7108
chore: split off accounts controller changes
hmalik88 Oct 23, 2025
2df0f84
chore: split off keyring controller changes
hmalik88 Oct 23, 2025
9f6890d
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Oct 23, 2025
9e44c34
refactor: apply code review
hmalik88 Oct 29, 2025
ba51a39
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Oct 29, 2025
37f79b6
refactor: changed logic to clear entire virtual list for a provider w…
hmalik88 Oct 29, 2025
f56a14a
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Nov 3, 2025
a1855a6
refactor: make changes after rebase
hmalik88 Nov 3, 2025
db00bc8
fix: more fix after rebase
hmalik88 Nov 3, 2025
9fc2ee2
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Nov 5, 2025
5e076b3
fix: update tests
hmalik88 Nov 6, 2025
f8a7783
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Nov 6, 2025
a414253
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Nov 11, 2025
c3b8390
fix: update tests
hmalik88 Nov 11, 2025
6bb050e
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Nov 11, 2025
612a846
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Nov 14, 2025
483da6e
refactor: apply code review
hmalik88 Nov 14, 2025
b746565
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Nov 14, 2025
602ce8f
fix: lint fix
hmalik88 Nov 14, 2025
a5746db
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Nov 23, 2025
981817a
fix: add override init method
hmalik88 Nov 24, 2025
01c594c
refactor: apply code review
hmalik88 Nov 25, 2025
2a4c71f
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Nov 25, 2025
7334bbc
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Nov 25, 2025
0e24c48
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Nov 25, 2025
ea00380
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Nov 26, 2025
0a0db20
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Dec 1, 2025
82fa21d
add remove account actions
hmalik88 Jan 7, 2026
bcd998a
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Jan 7, 2026
21c74ac
fix: update tests
hmalik88 Jan 8, 2026
550c11f
feat: add param to remove account wallet method
hmalik88 Jan 8, 2026
8e22d1d
fix: update tests
hmalik88 Jan 8, 2026
a7483ff
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Jan 8, 2026
a6d4432
feat: add messenger registration for removeMultichainAccountWallet an…
hmalik88 Jan 8, 2026
1881a2c
fix: lint fixes
hmalik88 Jan 8, 2026
b747e5f
fix: more lint fixes
hmalik88 Jan 8, 2026
770e25f
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Jan 8, 2026
7d64234
fix: test fixes
hmalik88 Jan 8, 2026
176a79b
fix: fix naming
hmalik88 Jan 8, 2026
73add20
chore: update suppressions
hmalik88 Jan 8, 2026
8b0cc36
fix: prettier fix
hmalik88 Jan 8, 2026
cb57f4c
fix: move clearAccountToProviderState logic to only clear provider st…
hmalik88 Jan 9, 2026
6bf879c
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Jan 9, 2026
6158c7f
chore: update jsdoc for remove wallet method
hmalik88 Jan 9, 2026
a2ea0f1
feat: add logic to init a newly created wallet
hmalik88 Jan 9, 2026
66051a5
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Jan 9, 2026
6a56850
fix: update changelog
hmalik88 Jan 9, 2026
f7eec3f
fix: make sure we're creating the first account on import
hmalik88 Jan 14, 2026
5288909
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Jan 14, 2026
b96fd74
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Jan 15, 2026
fd4aea5
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Jan 15, 2026
0e4b151
fix: update logic to create first multichain group in newly created w…
hmalik88 Jan 17, 2026
4a66b65
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Jan 17, 2026
a8c05bb
fix: add another fix to prevent hanging promise
hmalik88 Jan 17, 2026
903db2d
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Jan 20, 2026
1142ae4
Merge remote-tracking branch 'origin/main' into hm/bip-44-perf-and-de…
hmalik88 Jan 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/multichain-account-service/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- **BREAKING** A performance refactor was made around all the classes in this package ([#6654](https://github.com/MetaMask/core/pull/6654))
- Add logic in the `createMultichainAccountWallet` method in `MultichainAccountService` so that it can handle all entry points: importing an SRP, recovering a vault and creating a new vault.
- Add a `getAccountIds` method which returns all the account ids pertaining to a group.
- Add an `addAccounts` method on the `BaseBip44AccountProvider` class which keeps track of all the account IDs that pertain to it.
- Wait for Snap platform to be ready before any wallet/group operations ([#7266](https://github.com/MetaMask/core/pull/7266))
- Add `SnapAccountProvider.withSnap` protected helper ([#7266](https://github.com/MetaMask/core/pull/7266))
- This is used to protect any Snap operation behind a guard that checks if the Snap platform is ready.
Expand Down Expand Up @@ -106,8 +110,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- **BREAKING** A performance refactor was made around all the classes in this package ([#6654](https://github.com/MetaMask/core/pull/6654))
- The `MultichainAccountService` is refactored to construct a top level service state for its `init` function, this state is passed down to the `MultichainAccountWallet` and `MultichainAccountGroup` classes in slices for them to construct their internal states.
- Additional state is generated at the entry points where it needs to be updated i.e. `createMultichainAccountGroup`, `discoverAccounts` and `alignAccounts`.
- We no longer prevent group creation if some providers' `createAccounts` calls fail during group creation, only if they all fail.
- The `getAccounts` method in the `BaseBip44AccountProvider` class no longer relies on fetching the entire list of internal accounts from the `AccountsController`, instead it gets the specific accounts that it stores in its internal accounts list.
- The `EvmAccountProvider` no longer fetches from the `AccountController` to get an account for its ID, we deterministically get the associated account ID through `getUUIDFromAddressOfNormalAccount`.
- The `EvmAccountProvider` now uses the `getAccount` method from the `AccountsController` when fetching an account after account creation as it is more efficient.
- Bump `@metamask/base-controller` from `^8.4.1` to `^8.4.2` ([#6917](https://github.com/MetaMask/core/pull/6917))

### Removed

- **BREAKING** A performance refactor was made around all the classes in this package ([#6654](https://github.com/MetaMask/core/pull/6654))
- Remove `#handleOnAccountAdded` and `#handleOnAccountRemoved` methods in `MultichainAccountService` due to internal state being updated within the service.
- Remove `getAccountContext` (and associated map) in the `MultichainAccountService` as the service no longer uses that method.
- Remove the `sync` method in favor of the sole `init` method for both `MultichainAccountWallet` and `MultichainAccountGroup`.

## [1.6.1]

### Changed
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
import type { Bip44Account } from '@metamask/account-api';
import {
AccountGroupType,
isBip44Account,
toMultichainAccountGroupId,
toMultichainAccountWalletId,
} from '@metamask/account-api';
import { EthScope, SolScope } from '@metamask/keyring-api';
import type { InternalAccount } from '@metamask/keyring-internal-api';

import type { GroupState } from './MultichainAccountGroup';
import { MultichainAccountGroup } from './MultichainAccountGroup';
import { MultichainAccountWallet } from './MultichainAccountWallet';
import type { RootMessenger, MockAccountProvider } from './tests';
import {
MOCK_SNAP_ACCOUNT_2,
MOCK_WALLET_1_BTC_P2TR_ACCOUNT,
MOCK_WALLET_1_BTC_P2WPKH_ACCOUNT,
MOCK_WALLET_1_ENTROPY_SOURCE,
MOCK_WALLET_1_EVM_ACCOUNT,
MOCK_WALLET_1_SOL_ACCOUNT,
setupNamedAccountProvider,
setupBip44AccountProvider,
getMultichainAccountServiceMessenger,
getRootMessenger,
mockCreateAccountsOnce,
} from './tests';
import type { MockAccountProvider, RootMessenger } from './tests';
import type { MultichainAccountServiceMessenger } from './types';

function setup({
Expand All @@ -45,8 +48,11 @@ function setup({
providers: MockAccountProvider[];
messenger: MultichainAccountServiceMessenger;
} {
const providers = accounts.map((providerAccounts) => {
return setupNamedAccountProvider({ accounts: providerAccounts });
const providers = accounts.map((providerAccounts, idx) => {
return setupBip44AccountProvider({
name: `Provider ${idx + 1}`,
accounts: providerAccounts,
});
});

const serviceMessenger = getMultichainAccountServiceMessenger(messenger);
Expand All @@ -64,6 +70,18 @@ function setup({
messenger: serviceMessenger,
});

// Initialize group state from provided accounts so that constructor tests
// observe accounts immediately
const groupState = providers.reduce<GroupState>((state, provider, idx) => {
const ids = accounts[idx].filter(isBip44Account).map((a) => a.id);
if (ids.length > 0) {
state[provider.getName()] = ids;
}
return state;
}, {});

group.init(groupState);

return { wallet, group, providers, messenger: serviceMessenger };
}

Expand All @@ -88,6 +106,10 @@ describe('MultichainAccount', () => {
expect(group.type).toBe(AccountGroupType.MultichainAccount);
expect(group.groupIndex).toBe(groupIndex);
expect(group.wallet).toStrictEqual(wallet);
expect(group.hasAccounts()).toBe(true);
expect(group.getAccountIds()).toStrictEqual(
expectedAccounts.map((a) => a.id),
);
expect(group.getAccounts()).toHaveLength(expectedAccounts.length);
expect(group.getAccounts()).toStrictEqual(expectedAccounts);
});
Expand Down Expand Up @@ -160,62 +182,118 @@ describe('MultichainAccount', () => {
});

describe('alignAccounts', () => {
it('creates missing accounts only for providers with no accounts', async () => {
it('aligns accounts for all providers', async () => {
const groupIndex = 0;
const { group, providers, wallet } = setup({
groupIndex,
accounts: [
[MOCK_WALLET_1_EVM_ACCOUNT], // provider[0] already has group 0
[], // provider[1] missing group 0
],
accounts: [[MOCK_WALLET_1_EVM_ACCOUNT], []],
});

mockCreateAccountsOnce(providers[0], [MOCK_WALLET_1_EVM_ACCOUNT]);
mockCreateAccountsOnce(providers[1], [MOCK_WALLET_1_SOL_ACCOUNT]);

await group.alignAccounts();

expect(providers[0].createAccounts).not.toHaveBeenCalled();
expect(providers[0].createAccounts).toHaveBeenCalledWith({
entropySource: wallet.entropySource,
groupIndex,
});
expect(providers[1].createAccounts).toHaveBeenCalledWith({
entropySource: wallet.entropySource,
groupIndex,
});

expect(group.getAccounts()).toHaveLength(2);
expect(group.getAccounts()).toStrictEqual([
MOCK_WALLET_1_EVM_ACCOUNT,
MOCK_WALLET_1_SOL_ACCOUNT,
]);
});

it('does nothing when already aligned', async () => {
it('warns if alignment fails for a single provider', async () => {
const groupIndex = 0;
const { group, providers } = setup({
const { group, providers, wallet } = setup({
groupIndex,
accounts: [[MOCK_WALLET_1_EVM_ACCOUNT], [MOCK_WALLET_1_SOL_ACCOUNT]],
accounts: [[MOCK_WALLET_1_EVM_ACCOUNT], []],
});

const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
providers[1].createAccounts.mockRejectedValueOnce(
new Error('Unable to create accounts'),
);

await group.alignAccounts();

expect(providers[0].createAccounts).not.toHaveBeenCalled();
expect(providers[1].createAccounts).not.toHaveBeenCalled();
expect(providers[1].createAccounts).toHaveBeenCalledWith({
entropySource: wallet.entropySource,
groupIndex,
});
expect(consoleSpy).toHaveBeenCalledWith(
`Failed to fully align multichain account group for entropy ID: ${wallet.entropySource} and group index: ${groupIndex}, some accounts might be missing. Provider threw the following error:\n- Provider 2: Unable to create accounts`,
);
});

it('warns if provider alignment fails', async () => {
it('warns if alignment fails for multiple providers', async () => {
const groupIndex = 0;
const { group, providers, wallet } = setup({
groupIndex,
accounts: [[MOCK_WALLET_1_EVM_ACCOUNT], []],
accounts: [[MOCK_WALLET_1_EVM_ACCOUNT], [], []],
});

const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
providers[1].createAccounts.mockRejectedValueOnce(
new Error('Unable to create accounts'),
);

providers[2].createAccounts.mockRejectedValueOnce(
new Error('Unable to create accounts'),
);

await group.alignAccounts();

expect(providers[0].createAccounts).not.toHaveBeenCalled();
expect(providers[1].createAccounts).toHaveBeenCalledWith({
entropySource: wallet.entropySource,
groupIndex,
});
expect(providers[2].createAccounts).toHaveBeenCalledWith({
entropySource: wallet.entropySource,
groupIndex,
});
expect(consoleSpy).toHaveBeenCalledWith(
`Failed to fully align multichain account group for entropy ID: ${wallet.entropySource} and group index: ${groupIndex}, some accounts might be missing`,
`Failed to fully align multichain account group for entropy ID: ${wallet.entropySource} and group index: ${groupIndex}, some accounts might be missing. Providers threw the following errors:\n- Provider 2: Unable to create accounts\n- Provider 3: Unable to create accounts`,
);
});

it('does not create accounts when a provider is disabled', async () => {
const groupIndex = 0;
const { group, providers } = setup({
groupIndex,
accounts: [[], []],
});

providers[1].setEnabled(false);
await group.alignAccounts();

expect(providers[0].createAccounts).toHaveBeenCalled();
expect(providers[1].createAccounts).not.toHaveBeenCalled();
});

it('removes accounts from the group when a provider is disabled', async () => {
const groupIndex = 0;
const { group, providers } = setup({
groupIndex,
accounts: [[MOCK_WALLET_1_EVM_ACCOUNT], []],
});

providers[0].setEnabled(false);
await group.alignAccounts();

expect(providers[0].createAccounts).not.toHaveBeenCalled();
expect(providers[1].createAccounts).toHaveBeenCalled();

expect(group.getAccount(MOCK_WALLET_1_EVM_ACCOUNT.id)).toBeUndefined();
});

it('captures an error when a provider fails to create its account', async () => {
const groupIndex = 0;
const { group, providers, messenger } = setup({
Expand All @@ -227,7 +305,7 @@ describe('MultichainAccount', () => {
const captureExceptionSpy = jest.spyOn(messenger, 'captureException');
await group.alignAccounts();
expect(captureExceptionSpy).toHaveBeenCalledWith(
new Error('Unable to align accounts with provider "Mocked Provider"'),
new Error('Unable to align accounts with provider "Provider 2"'),
);
expect(captureExceptionSpy.mock.lastCall[0]).toHaveProperty(
'cause',
Expand Down
Loading
Loading