Skip to content

feat: Solana accountChanged event #15561

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

Open
wants to merge 69 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
f6174b7
feat: add MultichainRouter to Engine
ffmcgee725 May 21, 2025
faa8338
lint
ffmcgee725 May 21, 2025
de0e57b
minor lint
ffmcgee725 May 21, 2025
e389d3c
Merge branch 'main' into feat/sip-26-multichain-router
ffmcgee725 May 21, 2025
0237aae
feat: uncomment selector code for Solana dapp connectivity
ffmcgee725 May 21, 2025
82ca5dc
Merge branch 'main' into feat/sip-26-multichain-router
ffmcgee725 May 21, 2025
91e8edd
Merge branch 'main' into feat/sip-26-multichain-router
jiexi May 21, 2025
a29f430
refactor: address typescript issues
ffmcgee725 May 22, 2025
851ae07
chore: update controllers package
ffmcgee725 May 22, 2025
f6a4b65
merge main fix conflicts
ffmcgee725 May 22, 2025
cea7b77
lint
ffmcgee725 May 22, 2025
491d2ed
lint
ffmcgee725 May 22, 2025
7cc2e84
lint
ffmcgee725 May 22, 2025
bba1968
lint
ffmcgee725 May 22, 2025
dac3d96
suggestion
adonesky1 May 22, 2025
002bc98
suggestion (#15546)
ffmcgee725 May 22, 2025
8f3382a
test: fix Permissions test module
ffmcgee725 May 22, 2025
007eddd
fix: get non evm network data from proper controller state
ffmcgee725 May 22, 2025
20bf6b3
cleanup suggestion
adonesky1 May 22, 2025
3d615b6
cleanup suggestion (#15554)
ffmcgee725 May 22, 2025
98bdc29
Merge branch 'main' into feat/sip-26-multichain-router
ffmcgee725 May 22, 2025
300389d
test: temporary mock state fix for AccountConnect unit test
ffmcgee725 May 22, 2025
7166c0b
Merge branch 'main' into feat/sip-26-multichain-router
ffmcgee725 May 22, 2025
238e588
WIP
jiexi May 22, 2025
b5bb6c8
Merge branch 'main' into feat/sip-26-multichain-router
jiexi May 22, 2025
c5c90d0
Merge branch 'feat/sip-26-multichain-router' into jl/sip-26-multichai…
jiexi May 22, 2025
fa44aba
cleanup sorting
jiexi May 22, 2025
c819bda
Fix background bridge
jiexi May 22, 2025
2787ca0
Fix downstream usage
jiexi May 22, 2025
d9686d5
lint
jiexi May 22, 2025
9d20be8
fix spec
jiexi May 22, 2025
aa66683
Merge branch 'main' into feat/sip-26-multichain-router
ffmcgee725 May 23, 2025
a583cce
fix: incorrect initial default network avatars shown in AccountConnec…
ffmcgee725 May 23, 2025
95541ca
Merge branch 'main' into feat/sip-26-multichain-router
ffmcgee725 May 23, 2025
9811cae
fix: proper UX for wallet:eip155 only request scope
ffmcgee725 May 23, 2025
1e280eb
merge main fix conflicts
ffmcgee725 May 23, 2025
d27d59d
Merge branch 'main' into feat/sip-26-multichain-router
ffmcgee725 May 23, 2025
ec40294
add sortMultichainAccountsByLastSelected spec
jiexi May 23, 2025
5ac587b
merge main fix conflicts
ffmcgee725 May 23, 2025
8950817
add accountChanged on connect
jiexi May 23, 2025
d2c85b4
Add bridge.notifySolanaAccountChangedForCurrentAccount spec
jiexi May 23, 2025
e458d11
add handleSolanaAccountChangedFromSelectedAccountChanges spec
jiexi May 23, 2025
a4bb258
lint
jiexi May 23, 2025
b6ad5d9
add handleSolanaAccountChangedFromScopeChanges spec
jiexi May 23, 2025
d23b3b5
Merge branch 'feat/sip-26-multichain-router' into jl/sip-26-multichai…
jiexi May 23, 2025
210d7db
Fix BrowserTab view editing permission from top right icon when only …
jiexi May 23, 2025
a5d0f44
Merge remote-tracking branch 'origin' into jl/sip-26-multichain-route…
adonesky1 May 27, 2025
85aac85
cleanup BackgroundBridge
jiexi May 28, 2025
0deda83
revert package.json
jiexi May 28, 2025
4b63ba3
fix account selection for non-evm
jiexi May 28, 2025
f19152d
fix snapshot and lint
jiexi May 28, 2025
0504863
tsc
jiexi May 28, 2025
c75e83e
Fix selected account in context of CAIP permission
jiexi May 28, 2025
a279d5e
fix sortMultichainAccountsByLastSelected spec
jiexi May 28, 2025
2d416bc
Merge branch 'main' into feat/sip-26-multichain-router
jiexi May 28, 2025
89a0ee7
Merge branch 'feat/sip-26-multichain-router' into jl/sip-26-multichai…
jiexi May 28, 2025
c3ebba2
lint
jiexi May 28, 2025
2adfd56
Revert bad lints
jiexi May 28, 2025
6535c61
lint
jiexi May 28, 2025
5276610
lint
jiexi May 28, 2025
861510c
Fix specs
jiexi May 28, 2025
2df9ec1
Fix all accounts shown as selected
jiexi May 29, 2025
4af4fdc
fix solana accountChanged announcement on page load
jiexi May 29, 2025
ec48774
lint
jiexi May 29, 2025
0afc798
Merge branch 'main' into feat/sip-26-multichain-router
jiexi May 29, 2025
9f5a9f0
Merge branch 'feat/sip-26-multichain-router' into jl/sip-26-multichai…
jiexi May 29, 2025
75ac09a
fix spec
jiexi May 29, 2025
35eb004
Remove commented code in backgroundbridge
jiexi May 30, 2025
8dd30f4
Update notifySolanaAccountChangedForCurrentAccount on pipeline setup
jiexi May 30, 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
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { Account, Assets } from '../../hooks/useAccounts';
import Engine from '../../../core/Engine';
import {
removeAccountsFromPermissions,
sortAccountsByLastSelected,
sortMultichainAccountsByLastSelected,
} from '../../../core/Permissions';
import Routes from '../../../constants/navigation/Routes';

Expand All @@ -44,8 +44,7 @@ import { WalletViewSelectorsIDs } from '../../../../e2e/selectors/wallet/WalletV
import { RootState } from '../../../reducers';
import { ACCOUNT_SELECTOR_LIST_TESTID } from './CaipAccountSelectorList.constants';
import { toHex } from '@metamask/controller-utils';
import { CaipAccountId, Hex } from '@metamask/utils';
import { parseAccountId } from '@walletconnect/utils';
import { CaipAccountId, parseCaipAccountId } from '@metamask/utils';

const CaipAccountSelectorList = ({
onSelectAccount,
Expand Down Expand Up @@ -155,21 +154,15 @@ const CaipAccountSelectorList = ({
const nextCaipAccountIds = selectedAddresses.filter(
(selectedAddress) => selectedAddress !== caipAccountId,
);
const nextAddresses = nextCaipAccountIds.map(
(nextCaipAccountId) => {
const { address: nextAddress } =
parseAccountId(nextCaipAccountId);
return nextAddress as Hex;
},
);
const [nextCaipAccountId] =
sortMultichainAccountsByLastSelected(nextCaipAccountIds);

const nextAddressesSorted =
sortAccountsByLastSelected(nextAddresses);
const nextAddress = nextCaipAccountId ? parseCaipAccountId(nextCaipAccountId).address : '';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens when the logic falls back to empty string and onRemoveImportedAccount is invoked? Nothing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nothing, lol. removeAccount isn't even used in the handler apparently

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

opting to just leave this as is since it's the similar in EvmAccountSelectorList

const selectedAccountAddress = accounts.find(
(acc) => acc.isSelected,
)?.address;
nextActiveAddress =
nextAddressesSorted[0] || selectedAccountAddress || '';
nextAddress || selectedAccountAddress || '';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question as above

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nothing happens if nextActiveAddress is falsy

nextActiveAddress && Engine.setSelectedAddress(nextActiveAddress);

}

// Switching accounts on the PreferencesController must happen before account is removed from the KeyringController, otherwise UI will break.
Expand Down
2 changes: 1 addition & 1 deletion app/components/Views/AccountConnect/AccountConnect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ const AccountConnect = (props: AccountConnectProps) => {
),
},
};

const connectedAccountLength = selectedAddresses.length;
const activeAddress = selectedAddresses[0];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ jest.mock('../../../core/Engine', () => ({
},
AccountsController: {
listAccounts: jest.fn(() => MOCK_USE_ACCOUNTS_RETURN),
listMultichainAccounts: jest.fn(() => MOCK_INTERNAL_ACCOUNTS),
getAccountByAddress: jest.fn((address: string) =>
MOCK_INTERNAL_ACCOUNTS.find((account) => account.address === address),
),
Expand Down Expand Up @@ -240,13 +241,6 @@ const mockInitialState = (
},
});

const evmNetworkConfigurationsByChainId =
mockInitialState().engine?.backgroundState?.NetworkController
?.networkConfigurationsByChainId;
const nonEvmNetworkConfigurationsByChainId =
mockInitialState().engine?.backgroundState?.MultichainNetworkController
?.multichainNetworkConfigurationsByChainId;

describe('AccountPermissions', () => {
beforeEach(() => {
mockUpdatePermittedChains.mockReset();
Expand Down Expand Up @@ -426,8 +420,6 @@ describe('AccountPermissions', () => {
expect(mockAddPermittedAccounts).toHaveBeenCalledWith(
'test',
['eip155:0:0xd018538C87232FF95acbCe4870629b75640a78E7'],
evmNetworkConfigurationsByChainId,
nonEvmNetworkConfigurationsByChainId,
);
});

Expand Down Expand Up @@ -457,8 +449,6 @@ describe('AccountPermissions', () => {
expect(mockAddPermittedAccounts).toHaveBeenCalledWith(
'test',
['eip155:0:0xd018538C87232FF95acbCe4870629b75640a78E7'],
evmNetworkConfigurationsByChainId,
nonEvmNetworkConfigurationsByChainId,
);
expect(mockRemovePermittedAccounts).not.toHaveBeenCalled();
});
Expand Down Expand Up @@ -527,8 +517,6 @@ describe('AccountPermissions', () => {
expect(mockAddPermittedAccounts).toHaveBeenCalledWith(
'test',
['eip155:0:0xd018538C87232FF95acbCe4870629b75640a78E7'],
evmNetworkConfigurationsByChainId,
nonEvmNetworkConfigurationsByChainId,
);
expect(mockRemovePermittedAccounts).toHaveBeenCalledWith('test', [
'0xC4955C0d639D99699Bfd7Ec54d9FaFEe40e4D272',
Expand Down
20 changes: 10 additions & 10 deletions app/components/Views/AccountPermissions/AccountPermissions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
getPermittedCaipAccountIdsByHostname,
removePermittedAccounts,
getPermittedCaipChainIdsByHostname,
sortMultichainAccountsByLastSelected,
} from '../../../core/Permissions';
import AccountConnectMultiSelector from '../AccountConnect/AccountConnectMultiSelector';
import NetworkConnectMultiSelector from '../NetworkConnect/NetworkConnectMultiSelector';
Expand Down Expand Up @@ -87,7 +88,6 @@ import { parseChainId } from '@walletconnect/utils';
import { NetworkConfiguration } from '@metamask/network-controller';
import { NetworkAvatarProps } from '../AccountConnect/AccountConnect.types';
import styleSheet from './AccountPermissions.styles';
import { selectNonEvmNetworkConfigurationsByChainId } from '../../../selectors/multichainNetworkController';

const AccountPermissions = (props: AccountPermissionsProps) => {
const { navigate } = useNavigation();
Expand All @@ -109,8 +109,6 @@ const AccountPermissions = (props: AccountPermissionsProps) => {

const accountsLength = useSelector(selectAccountsLength);
const currentEvmChainId = useSelector(selectEvmChainId);
const evmNetworkConfigurationsByChainId = useSelector(selectEvmNetworkConfigurationsByChainId);
const nonEvmNetworkConfigurationsByChainId = useSelector(selectNonEvmNetworkConfigurationsByChainId);
const networkInfo = useNetworkInfo(hostname);
const nonTestnetNetworks = useSelector(
(state: RootState) =>
Expand Down Expand Up @@ -138,7 +136,8 @@ const AccountPermissions = (props: AccountPermissionsProps) => {
permittedAccountsList,
hostname,
);
const permittedCaipAccountIds = uniq(
const permittedCaipAccountIds = useMemo(() => {
const unsortedPermittedAccounts = uniq(
nonRemappedPermittedAccounts.map((caipAccountId) => {
const {
address,
Expand All @@ -149,8 +148,10 @@ const AccountPermissions = (props: AccountPermissionsProps) => {
return `eip155:0:${address}` as CaipAccountId;
}
return caipAccountId;
}),
);
}));

return sortMultichainAccountsByLastSelected(unsortedPermittedAccounts);
}, [nonRemappedPermittedAccounts]);

const permittedCaipChainIds = getPermittedCaipChainIdsByHostname(
permittedAccountsList,
Expand Down Expand Up @@ -424,7 +425,7 @@ const AccountPermissions = (props: AccountPermissionsProps) => {
);

if (accountsToAdd.length > 0) {
addPermittedAccounts(hostname, accountsToAdd, evmNetworkConfigurationsByChainId, nonEvmNetworkConfigurationsByChainId);
addPermittedAccounts(hostname, accountsToAdd);
newPermittedAccounts = [...newPermittedAccounts, ...accountsToAdd];
}

Expand Down Expand Up @@ -484,8 +485,6 @@ const AccountPermissions = (props: AccountPermissionsProps) => {
}
},
[
evmNetworkConfigurationsByChainId,
nonEvmNetworkConfigurationsByChainId,
permittedCaipAccountIds,
setIsLoading,
hostname,
Expand Down Expand Up @@ -623,7 +622,8 @@ const AccountPermissions = (props: AccountPermissionsProps) => {
onDismissSheet={hideSheet}
accounts={accountsFilteredByPermissions.permitted}
ensByAccountAddress={ensByAccountAddress}
selectedAddresses={permittedCaipAccountIds}
// This is only okay because permittedCaipAccountIds is sorted by lastSelected already
selectedAddresses={permittedCaipAccountIds.length > 0 ? [permittedCaipAccountIds[0]] : []}
favicon={faviconSource}
hostname={hostname}
urlWithProtocol={urlWithProtocol}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { View } from 'react-native';
// External dependencies.
import SheetActions from '../../../../component-library/components-temp/SheetActions';
import { strings } from '../../../../../locales/i18n';
import EvmAccountSelectorList from '../../../../components/UI/EvmAccountSelectorList';
import { AccountPermissionsScreens } from '../AccountPermissions.types';
import {
ToastContext,
Expand All @@ -30,6 +29,8 @@ import Button, {
ButtonWidthTypes,
} from '../../../../component-library/components/Buttons/Button';
import Engine from '../../../../core/Engine';
import CaipAccountSelectorList from '../../../UI/CaipAccountSelectorList';
import { CaipAccountId, parseCaipAccountId } from '@metamask/utils';

const AccountPermissionsConnected = ({
ensByAccountAddress,
Expand All @@ -42,21 +43,19 @@ const AccountPermissionsConnected = ({
favicon,
accountAvatarType,
}: AccountPermissionsConnectedProps) => {
const activeAddress = selectedAddresses[0];
const { toastRef } = useContext(ToastContext);

const onConnectMoreAccounts = useCallback(() => {
onSetPermissionsScreen(AccountPermissionsScreens.ConnectMoreAccounts);
}, [onSetPermissionsScreen]);

const switchActiveAccount = useCallback(
(address: string) => {
if (address !== activeAddress) {
Engine.setSelectedAddress(address);
}
(caipAccountId: CaipAccountId) => {
const {address} = parseCaipAccountId(caipAccountId);
Engine.setSelectedAddress(address);
onDismissSheet();
const activeAccountName = getAccountNameWithENS({
accountAddress: address,
caipAccountId,
accounts,
ensByAccountAddress,
});
Expand All @@ -75,7 +74,6 @@ const AccountPermissionsConnected = ({
});
},
[
activeAddress,
onDismissSheet,
accounts,
ensByAccountAddress,
Expand Down Expand Up @@ -120,7 +118,7 @@ const AccountPermissionsConnected = ({
{strings('accounts.connected_accounts_title')}
</Text>
</View>
<EvmAccountSelectorList
<CaipAccountSelectorList
onSelectAccount={switchActiveAccount}
accounts={accounts}
ensByAccountAddress={ensByAccountAddress}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import { UseAccounts } from '../../../hooks/useAccounts';
import { AccountPermissionsScreens } from '../AccountPermissions.types';
import { IconName } from '../../../../component-library/components/Icons/Icon';
import { AvatarAccountType } from '../../../../component-library/components/Avatars/Avatar/variants/AvatarAccount';
import { CaipAccountId } from '@metamask/utils';

/**
* AccountPermissionsConnected props.
*/
export interface AccountPermissionsConnectedProps
extends Omit<UseAccounts, 'evmAccounts'> {
isLoading?: boolean;
selectedAddresses: string[];
selectedAddresses: CaipAccountId[];
onSetPermissionsScreen: (screen: AccountPermissionsScreens) => void;
onDismissSheet: () => void;
hostname: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ exports[`AccountPermissions renders correctly 1`] = `
style={
{
"alignItems": "center",
"backgroundColor": "#ffffff",
"backgroundColor": "#4459ff1a",
"flexDirection": "row",
}
}
Expand Down Expand Up @@ -569,6 +569,34 @@ exports[`AccountPermissions renders correctly 1`] = `
</View>
</View>
</View>
<View
accessibilityRole="checkbox"
accessible={true}
style={
{
"backgroundColor": "#4459ff1a",
"bottom": 0,
"flexDirection": "row",
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
"width": 4,
}
}
>
<View
style={
{
"backgroundColor": "#4459ff",
"borderRadius": 2,
"marginLeft": 4,
"marginVertical": 4,
"width": 4,
}
}
/>
</View>
</TouchableOpacity>
<View
style={
Expand Down
8 changes: 4 additions & 4 deletions app/components/Views/Browser/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,12 +208,12 @@ const homePageUrl = useCallback(

useEffect(() => {
const checkIfActiveAccountChanged = (hostnameForToastCheck) => {
const permittedAccounts = getPermittedAccounts(hostnameForToastCheck);
const activeAccountAddress = permittedAccounts?.[0];
const permittedEvmAccounts = getPermittedAccounts(hostnameForToastCheck);
const activeAccountAddress = permittedEvmAccounts?.[0];

if (activeAccountAddress) {
const accountName = getAccountNameWithENS({
accountAddress: activeAccountAddress,
caipAccountId: `eip155:0:${activeAccountAddress}`,
accounts,
ensByAccountAddress,
});
Expand Down Expand Up @@ -495,4 +495,4 @@ Browser.propTypes = {

export { default as createBrowserNavDetails } from './Browser.types';

export default connect(mapStateToProps, mapDispatchToProps)(Browser);
export default connect(mapStateToProps, mapDispatchToProps)(Browser);
11 changes: 6 additions & 5 deletions app/components/Views/Browser/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -275,12 +275,13 @@ describe('Browser', () => {

// Mock accounts and ENS data
const mockAccounts = [
{
address: testAccountAddress,
{
address: testAccountAddress,
name: mockAccountName,
type: KeyringTypes.simple,
yOffset: 0,
isSelected: true
isSelected: true,
caipAccountId: `eip155:0:${testAccountAddress}`
}
];
const mockEnsByAccountAddress = {
Expand Down Expand Up @@ -365,8 +366,8 @@ describe('Browser', () => {
const mockAccountName2 = 'Account 2';

const mockAccounts = [
{ address: testAccountAddress1, name: mockAccountName1, type: KeyringTypes.simple, yOffset: 0, isSelected: true },
{ address: testAccountAddress2, name: mockAccountName2, type: KeyringTypes.simple, yOffset: 0, isSelected: false },
{ address: testAccountAddress1, name: mockAccountName1, type: KeyringTypes.simple, yOffset: 0, isSelected: true, caipAccountId: `eip155:0:${testAccountAddress1}` },
{ address: testAccountAddress2, name: mockAccountName2, type: KeyringTypes.simple, yOffset: 0, isSelected: false, caipAccountId: `eip155:0:${testAccountAddress2}` },
];
const mockEnsByAccountAddress = {
[testAccountAddress1]: 'account1.eth',
Expand Down
12 changes: 7 additions & 5 deletions app/components/Views/BrowserTab/BrowserTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import {
getCaip25Caveat,
getPermittedCaipAccountIdsByHostname,
getPermittedEvmAddressesByHostname,
sortMultichainAccountsByLastSelected,
} from '../../../core/Permissions';
import Routes from '../../../constants/navigation/Routes';
import {
Expand Down Expand Up @@ -187,7 +188,7 @@ export const BrowserTab: React.FC<BrowserTabProps> = React.memo(({
const backgroundBridgeRef = useRef<{
url: string;
hostname: string;
sendNotification: (payload: unknown) => void;
sendNotificationEip1193: (payload: unknown) => void;
onDisconnect: () => void;
onMessage: (message: Record<string, unknown>) => void;
}>();
Expand All @@ -211,10 +212,11 @@ export const BrowserTab: React.FC<BrowserTabProps> = React.memo(({
permissionsControllerState,
hostname,
);
const permittedAccountAddresses = permittedAccountIds.map((accountId) => {
const { address } = parseCaipAccountId(accountId)
const sortedPermittedAccountIds = sortMultichainAccountsByLastSelected(permittedAccountIds);
const permittedAccountAddresses = sortedPermittedAccountIds.map((accountId) => {
const { address } = parseCaipAccountId(accountId);
return address;
})
});
return permittedAccountAddresses;
}, isEqual);

Expand Down Expand Up @@ -248,7 +250,7 @@ export const BrowserTab: React.FC<BrowserTabProps> = React.memo(({
}, []);

const notifyAllConnections = useCallback((payload: unknown) => {
backgroundBridgeRef.current?.sendNotification(payload);
backgroundBridgeRef.current?.sendNotificationEip1193(payload);
}, []);

/**
Expand Down
Loading
Loading