Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add MultichainNetworkController to handle both EVM and non-EVM network and account switching #5215

Open
wants to merge 46 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
47c7310
feat add multichain-network-controller package
gantunesr Jan 24, 2025
7b64a8c
chore: update exports
gantunesr Jan 24, 2025
37dc7c5
fix: build
gantunesr Jan 24, 2025
454c3db
chore: update initial state
gantunesr Jan 27, 2025
2f4d06c
add setter for update nonEvmNetwork state variable to true and setter…
tommasini Jan 29, 2025
4ee7b32
merge main and solve yarn lock file conflicts
tommasini Jan 29, 2025
2f8058a
added blank line between functions
tommasini Jan 29, 2025
6b7b3b6
removed unused import and re order imports, to fix lint warnings
tommasini Jan 29, 2025
8a5c9d0
Handle network and account updates when switching networks
Cal-L Jan 29, 2025
f898056
Register message handlers
Cal-L Jan 29, 2025
edb6868
Separate concerns when updating network on multichain network controller
Cal-L Jan 30, 2025
1c3dc66
Handle switching network when account changes
Cal-L Jan 30, 2025
c1050f7
Remove unused actions and events
Cal-L Jan 30, 2025
a89bff4
btc and solana chain id from keyring api, update types and added java…
tommasini Jan 30, 2025
f159822
Merge branch 'feat/multichain-networks-controller' of github.com:Meta…
Cal-L Jan 30, 2025
dab430b
Remove draft type
Cal-L Jan 30, 2025
6994dbe
Fix account change subscription in multichain network controller
Cal-L Jan 30, 2025
ebe7e29
Add comment
Cal-L Jan 30, 2025
20a1dbc
replace nativeAsset name varialbe for nativeCurrency name variable
tommasini Jan 31, 2025
063e25a
Merge branch 'feat/multichain-networks-controller' of github.com:Meta…
Cal-L Jan 31, 2025
cf31cc3
git commit -m "Duplicate internal accounts mock"
Cal-L Feb 4, 2025
429f6ad
Update package deps
Cal-L Feb 4, 2025
a1618ab
Clean up MultichainNetworkController
Cal-L Feb 4, 2025
7f1ecd9
Update constants
Cal-L Feb 4, 2025
ba0d1ea
Update branch coverage threshold
Cal-L Feb 4, 2025
f0d9e53
Update yarn lock
Cal-L Feb 4, 2025
81b318e
Handle multichain network change on accounts controller
Cal-L Feb 4, 2025
85fcbb5
Increase unit test coverage on util
Cal-L Feb 4, 2025
a3b670b
Increase test coverage for MultichainNetworkController
Cal-L Feb 4, 2025
22f0511
Increase test coverage for accounts controller
Cal-L Feb 4, 2025
51ed3fa
Simplify tests
Cal-L Feb 4, 2025
1fef6c2
Remove unused import
Cal-L Feb 4, 2025
b5dc0eb
Fix all lint issues
Cal-L Feb 4, 2025
2a51326
Fix unit tests
Cal-L Feb 4, 2025
b0d8baf
Remove empty object
Cal-L Feb 4, 2025
a2dbe43
Remove ts expect for build to pass
Cal-L Feb 4, 2025
0f6d846
Merge branch 'main' of github.com:MetaMask/core into feat/handle-mult…
Cal-L Feb 4, 2025
107d1d5
Add missing condition when switching accounts
Cal-L Feb 4, 2025
2c65ccd
Update package versions
Cal-L Feb 4, 2025
a4fbabc
Keep consistent accounts controller version
Cal-L Feb 4, 2025
3c653e6
Make utils package consistent
Cal-L Feb 4, 2025
a9f942f
Update imports from keyring-api
Cal-L Feb 4, 2025
1a1abeb
Fix import
Cal-L Feb 4, 2025
dcca502
Remove lint warnings for utils
Cal-L Feb 4, 2025
f86d786
chore: update network types (#5277)
gantunesr Feb 5, 2025
c58256d
chore: add block explorer URLs (#5288)
gantunesr Feb 5, 2025
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
3 changes: 0 additions & 3 deletions eslint-warning-thresholds.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@
"packages/accounts-controller/src/AccountsController.test.ts": {
"import-x/namespace": 1
},
"packages/accounts-controller/src/utils.ts": {
"jsdoc/tag-lines": 3
},
"packages/address-book-controller/src/AddressBookController.ts": {
"jsdoc/check-tag-names": 13
},
Expand Down
173 changes: 113 additions & 60 deletions packages/accounts-controller/src/AccountsController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
} from '@metamask/keyring-internal-api';
import type { SnapControllerState } from '@metamask/snaps-controllers';
import { SnapStatus } from '@metamask/snaps-utils';
import type { CaipChainId } from '@metamask/utils';
import { type CaipChainId } from '@metamask/utils';
import * as uuid from 'uuid';

Check warning on line 23 in packages/accounts-controller/src/AccountsController.test.ts

View workflow job for this annotation

GitHub Actions / Lint, build, and test / Lint (20.x)

No exported names found in module 'uuid'
import type { V4Options } from 'uuid';

import type {
Expand Down Expand Up @@ -307,6 +307,7 @@
'SnapKeyring:accountAssetListUpdated',
'SnapKeyring:accountBalancesUpdated',
'SnapKeyring:accountTransactionsUpdated',
'MultichainNetworkController:setActiveNetwork',
],
allowedActions: [
'KeyringController:getAccounts',
Expand Down Expand Up @@ -339,6 +340,10 @@
AccountsControllerActions | AllowedActions,
AccountsControllerEvents | AllowedEvents
>;
triggerMultichainNetworkChange: (args: {
evmClientId?: string;
nonEvmChainId?: CaipChainId;
}) => void;
} {
const accountsControllerMessenger =
buildAccountsControllerMessenger(messenger);
Expand All @@ -347,10 +352,47 @@
messenger: accountsControllerMessenger,
state: { ...defaultState, ...initialState },
});
return { accountsController, messenger };

const triggerMultichainNetworkChange = ({
evmClientId,
nonEvmChainId,
}: {
evmClientId?: string;
nonEvmChainId?: CaipChainId;
}) => {
messenger.publish('MultichainNetworkController:setActiveNetwork', {
evmClientId,
nonEvmChainId,
});
};

return { accountsController, messenger, triggerMultichainNetworkChange };
}

describe('AccountsController', () => {
const mockBtcAccount = createExpectedInternalAccount({
id: 'mock-non-evm',
name: 'non-evm',
address: 'bc1qzqc2aqlw8nwa0a05ehjkk7dgt8308ac7kzw9a6',
keyringType: KeyringTypes.snap,
type: BtcAccountType.P2wpkh,
});

const mockOlderEvmAccount = createExpectedInternalAccount({
id: 'mock-id-1',
name: 'mock account 1',
address: 'mock-address-1',
keyringType: KeyringTypes.hd,
lastSelected: 11111,
});
const mockNewerEvmAccount = createExpectedInternalAccount({
id: 'mock-id-2',
name: 'mock account 2',
address: 'mock-address-2',
keyringType: KeyringTypes.hd,
lastSelected: 22222,
});

describe('onSnapStateChange', () => {
it('be used enable an account if the Snap is enabled and not blocked', async () => {
const messenger = buildMessenger();
Expand Down Expand Up @@ -1514,6 +1556,63 @@
});
});

describe('handle MultichainNetworkController:setActiveNetwork event', () => {
it('should update selected account to non-EVM account when switching to non-EVM network', () => {
const messenger = buildMessenger();
const { accountsController, triggerMultichainNetworkChange } =
setupAccountsController({
initialState: {
internalAccounts: {
accounts: {
[mockOlderEvmAccount.id]: mockOlderEvmAccount,
[mockNewerEvmAccount.id]: mockNewerEvmAccount,
[mockBtcAccount.id]: mockBtcAccount,
},
selectedAccount: mockNewerEvmAccount.id,
},
},
messenger,
});

// Triggered from network switch to Bitcoin mainnet
triggerMultichainNetworkChange({
nonEvmChainId: BtcScope.Mainnet,
});

// BTC account is now selected
expect(accountsController.state.internalAccounts.selectedAccount).toBe(
mockBtcAccount.id,
);
});

it('should update selected account to EVM account when switching to EVM network', () => {
const messenger = buildMessenger();
const { accountsController, triggerMultichainNetworkChange } =
setupAccountsController({
initialState: {
internalAccounts: {
accounts: {
[mockNewerEvmAccount.id]: mockNewerEvmAccount,
[mockBtcAccount.id]: mockBtcAccount,
},
selectedAccount: mockBtcAccount.id,
},
},
messenger,
});

// Triggered from network switch to Bitcoin mainnet
triggerMultichainNetworkChange({
evmClientId: EthScope.Mainnet,
});

// ETH mainnet account is now selected
expect(accountsController.state.internalAccounts.selectedAccount).toBe(
mockNewerEvmAccount.id,
);
});
});

describe('updateAccounts', () => {
const mockAddress1 = '0x123';
const mockAddress2 = '0x456';
Expand Down Expand Up @@ -2144,29 +2243,6 @@
});

describe('getSelectedAccount', () => {
const mockNonEvmAccount = createExpectedInternalAccount({
id: 'mock-non-evm',
name: 'non-evm',
address: 'bc1qzqc2aqlw8nwa0a05ehjkk7dgt8308ac7kzw9a6',
keyringType: KeyringTypes.snap,
type: BtcAccountType.P2wpkh,
});

const mockOlderEvmAccount = createExpectedInternalAccount({
id: 'mock-id-1',
name: 'mock account 1',
address: 'mock-address-1',
keyringType: KeyringTypes.hd,
lastSelected: 11111,
});
const mockNewerEvmAccount = createExpectedInternalAccount({
id: 'mock-id-2',
name: 'mock account 2',
address: 'mock-address-2',
keyringType: KeyringTypes.hd,
lastSelected: 22222,
});

it.each([
{
lastSelectedAccount: mockNewerEvmAccount,
Expand All @@ -2177,7 +2253,7 @@
expected: mockOlderEvmAccount,
},
{
lastSelectedAccount: mockNonEvmAccount,
lastSelectedAccount: mockBtcAccount,
expected: mockNewerEvmAccount,
},
])(
Expand All @@ -2189,7 +2265,7 @@
accounts: {
[mockOlderEvmAccount.id]: mockOlderEvmAccount,
[mockNewerEvmAccount.id]: mockNewerEvmAccount,
[mockNonEvmAccount.id]: mockNonEvmAccount,
[mockBtcAccount.id]: mockBtcAccount,
},
selectedAccount: lastSelectedAccount.id,
},
Expand All @@ -2205,9 +2281,9 @@
initialState: {
internalAccounts: {
accounts: {
[mockNonEvmAccount.id]: mockNonEvmAccount,
[mockBtcAccount.id]: mockBtcAccount,
},
selectedAccount: mockNonEvmAccount.id,
selectedAccount: mockBtcAccount.id,
},
},
});
Expand All @@ -2234,29 +2310,6 @@
});

describe('getSelectedMultichainAccount', () => {
const mockNonEvmAccount = createExpectedInternalAccount({
id: 'mock-non-evm',
name: 'non-evm',
address: 'bc1qzqc2aqlw8nwa0a05ehjkk7dgt8308ac7kzw9a6',
keyringType: KeyringTypes.snap,
type: BtcAccountType.P2wpkh,
});

const mockOlderEvmAccount = createExpectedInternalAccount({
id: 'mock-id-1',
name: 'mock account 1',
address: 'mock-address-1',
keyringType: KeyringTypes.hd,
lastSelected: 11111,
});
const mockNewerEvmAccount = createExpectedInternalAccount({
id: 'mock-id-2',
name: 'mock account 2',
address: 'mock-address-2',
keyringType: KeyringTypes.hd,
lastSelected: 22222,
});

it.each([
{
chainId: undefined,
Expand All @@ -2265,18 +2318,18 @@
},
{
chainId: undefined,
selectedAccount: mockNonEvmAccount,
expected: mockNonEvmAccount,
selectedAccount: mockBtcAccount,
expected: mockBtcAccount,
},
{
chainId: 'eip155:1',
selectedAccount: mockNonEvmAccount,
selectedAccount: mockBtcAccount,
expected: mockNewerEvmAccount,
},
{
chainId: 'bip122:000000000019d6689c085ae165831e93',
selectedAccount: mockNonEvmAccount,
expected: mockNonEvmAccount,
selectedAccount: mockBtcAccount,
expected: mockBtcAccount,
},
])(
"chainId $chainId with selectedAccount '$selectedAccount.id' should return $expected.id",
Expand All @@ -2287,7 +2340,7 @@
accounts: {
[mockOlderEvmAccount.id]: mockOlderEvmAccount,
[mockNewerEvmAccount.id]: mockNewerEvmAccount,
[mockNonEvmAccount.id]: mockNonEvmAccount,
[mockBtcAccount.id]: mockBtcAccount,
},
selectedAccount: selectedAccount.id,
},
Expand All @@ -2312,9 +2365,9 @@
accounts: {
[mockOlderEvmAccount.id]: mockOlderEvmAccount,
[mockNewerEvmAccount.id]: mockNewerEvmAccount,
[mockNonEvmAccount.id]: mockNonEvmAccount,
[mockBtcAccount.id]: mockBtcAccount,
},
selectedAccount: mockNonEvmAccount.id,
selectedAccount: mockBtcAccount.id,
},
},
});
Expand Down
Loading
Loading