Repository: MetaMask/metamask-mobile
Pull request #15104: feat: account modal changes to switch account type
Author: login: jpuri
Labels: name: team-confirmations
description: Push issues to confirmations team
name: release-7.47.0
description: Issue or pull request that will be included in release 7.47.0
Created: 2025-05-05T03:03:30Z | Merged: 2025-05-06T15:58:37Z

## **Description**

Account modal changes to switch account type

## **Related issues**

Fixes: https://github.com/MetaMask/MetaMask-planning/issues/4802

## **Manual testing steps**

1. Enable functionality locally
2. Go to account modal and click option to switch account type
3. Check the modal displayed

## **Screenshots/Recordings**
https://github.com/user-attachments/assets/b7b1e9a8-6e30-48fe-8042-f8238ed7a9ea

## **Pre-merge author checklist**

- [X] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [X] I've completed the PR template to the best of my ability
- [X] I’ve included tests if applicable
- [X] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable
- [X] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.



Diff:
diff --git a/app/component-library/components-temp/Price/AggregatedPercentage/NonEvmAggregatedPercentage.test.tsx b/app/component-library/components-temp/Price/AggregatedPercentage/NonEvmAggregatedPercentage.test.tsx
index 97a34d74bfa..e30aae11564 100644
--- a/app/component-library/components-temp/Price/AggregatedPercentage/NonEvmAggregatedPercentage.test.tsx
+++ b/app/component-library/components-temp/Price/AggregatedPercentage/NonEvmAggregatedPercentage.test.tsx
@@ -129,7 +129,7 @@ describe('NonEvmAggregatedPercentage', () => {
     expect(getByText('(+1.25%)')).toBeTruthy();
     expect(getByText('+$0.20')).toBeTruthy();
 
-     expect(getByText('(+1.25%)').props.style).toMatchObject({
+    expect(getByText('(+1.25%)').props.style).toMatchObject({
       color: mockTheme.colors.success.default,
     });
   });
@@ -170,4 +170,4 @@ describe('NonEvmAggregatedPercentage', () => {
     expect(formattedPercentage.props.children).toBe('••••••••••');
     expect(formattedValuePrice.props.children).toBe('••••••••••');
   });
-});
\ No newline at end of file
+});
diff --git a/app/components/Nav/App/App.tsx b/app/components/Nav/App/App.tsx
index 5cc1c8881fd..e6338b0ff5d 100644
--- a/app/components/Nav/App/App.tsx
+++ b/app/components/Nav/App/App.tsx
@@ -136,6 +136,7 @@ import { SelectSRPBottomSheet } from '../../Views/SelectSRP/SelectSRPBottomSheet
 import NavigationService from '../../../core/NavigationService';
 import ConfirmTurnOnBackupAndSyncModal from '../../UI/Identity/ConfirmTurnOnBackupAndSyncModal/ConfirmTurnOnBackupAndSyncModal';
 import AddNewAccount from '../../Views/AddNewAccount';
+import SwitchAccountTypeModal from '../../Views/confirmations/components/modals/switch-account-type-modal';
 
 const clearStackNavigatorOptions = {
   headerShown: false,
@@ -569,6 +570,21 @@ const ModalConfirmationRequest = () => (
   </Stack.Navigator>
 );
 
+const ModalSwitchAccountType = () => (
+  <Stack.Navigator
+    screenOptions={{
+      headerShown: false,
+      cardStyle: { backgroundColor: importedColors.transparent },
+    }}
+    mode={'modal'}
+  >
+    <Stack.Screen
+      name={Routes.CONFIRMATION_SWITCH_ACCOUNT_TYPE}
+      component={SwitchAccountTypeModal}
+    />
+  </Stack.Navigator>
+);
+
 const AppFlow = () => {
   const userLoggedIn = useSelector(selectUserLoggedIn);
 
@@ -703,6 +719,10 @@ const AppFlow = () => {
         name={Routes.CONFIRMATION_REQUEST_MODAL}
         component={ModalConfirmationRequest}
       />
+      <Stack.Screen
+        name={Routes.CONFIRMATION_SWITCH_ACCOUNT_TYPE}
+        component={ModalSwitchAccountType}
+      />
     </Stack.Navigator>
   );
 };
diff --git a/app/components/Views/AccountActions/AccountActions.test.tsx b/app/components/Views/AccountActions/AccountActions.test.tsx
index 85a77d90e8e..4f12827db46 100644
--- a/app/components/Views/AccountActions/AccountActions.test.tsx
+++ b/app/components/Views/AccountActions/AccountActions.test.tsx
@@ -627,4 +627,32 @@ describe('AccountActions', () => {
       expect(revealButton).toBeNull();
     });
   });
+
+  describe('switch account type', () => {
+    it('displayed option to switch account type', () => {
+      const { getByText } = renderWithProvider(<AccountActions />, {
+        state: initialState,
+      });
+
+      expect(getByText('Switch to Smart account')).toBeTruthy();
+    });
+
+    it('calls navigate function when option to switch account type is clicked', () => {
+      const { getByText } = renderWithProvider(<AccountActions />, {
+        state: initialState,
+      });
+
+      fireEvent.press(getByText('Switch to Smart account'));
+
+      expect(mockNavigate).toHaveBeenCalledWith(
+        'ConfirmationSwitchAccountType',
+        {
+          params: {
+            address: '0x123',
+          },
+          screen: 'ConfirmationSwitchAccountType',
+        },
+      );
+    });
+  });
 });
diff --git a/app/components/Views/AccountActions/AccountActions.tsx b/app/components/Views/AccountActions/AccountActions.tsx
index 77f4aa7a60d..eaec567be08 100644
--- a/app/components/Views/AccountActions/AccountActions.tsx
+++ b/app/components/Views/AccountActions/AccountActions.tsx
@@ -220,6 +220,15 @@ const AccountActions = () => {
     });
   };
 
+  const goToSwitchAccountType = () => {
+    navigate(Routes.CONFIRMATION_SWITCH_ACCOUNT_TYPE, {
+      screen: Routes.CONFIRMATION_SWITCH_ACCOUNT_TYPE,
+      params: {
+        address: selectedAddress,
+      },
+    });
+  };
+
   ///: BEGIN:ONLY_INCLUDE_IF(multi-srp)
   const goToExportSRP = () => {
     sheetRef.current?.onCloseBottomSheet(() => {
@@ -492,6 +501,13 @@ const AccountActions = () => {
           )
           ///: END:ONLY_INCLUDE_IF
         }
+        {process.env.MM_SMART_ACCOUNT_UI_ENABLED && (
+          <AccountAction
+            actionTitle={strings('account_actions.switch_to_smart_account')}
+            iconName={IconName.SwapHorizontal}
+            onPress={goToSwitchAccountType}
+          />
+        )}
       </View>
       <BlockingActionModal
         modalVisible={blockingModalVisible}
diff --git a/app/components/Views/confirmations/components/modals/switch-account-type-modal/account-network-row/account-network-row.styles.ts b/app/components/Views/confirmations/components/modals/switch-account-type-modal/account-network-row/account-network-row.styles.ts
new file mode 100644
index 00000000000..f21f15bd9ca
--- /dev/null
+++ b/app/components/Views/confirmations/components/modals/switch-account-type-modal/account-network-row/account-network-row.styles.ts
@@ -0,0 +1,21 @@
+import { StyleSheet } from 'react-native';
+
+const styleSheet = () =>
+  StyleSheet.create({
+    wrapper: {
+      flexDirection: 'row',
+      alignItems: 'center',
+      justifyContent: 'space-between',
+      paddingHorizontal: 32,
+      paddingVertical: 4,
+    },
+    left_section: {
+      flexDirection: 'row',
+    },
+    name_section: {
+      flexDirection: 'column',
+      marginLeft: 16,
+    },
+  });
+
+export default styleSheet;
diff --git a/app/components/Views/confirmations/components/modals/switch-account-type-modal/account-network-row/account-network-row.test.tsx b/app/components/Views/confirmations/components/modals/switch-account-type-modal/account-network-row/account-network-row.test.tsx
new file mode 100644
index 00000000000..5e22cd4d01d
--- /dev/null
+++ b/app/components/Views/confirmations/components/modals/switch-account-type-modal/account-network-row/account-network-row.test.tsx
@@ -0,0 +1,46 @@
+import React from 'react';
+
+import renderWithProvider from '../../../../../../../util/test/renderWithProvider';
+import { EIP7702NetworkConfiguration } from '../../../../hooks/useEIP7702Networks';
+import AccountNetworkRow from './account-network-row';
+
+const MOCK_NETWORK = {
+  chainId: '0xaa36a7',
+  delegationAddress: '0x63c0c19a282a1b52b07dd5a65b58948a07dae32b',
+  isSupported: true,
+  upgradeContractAddress: '0x63c0c19a282a1B52b07dD5a65b58948A07DAE32B',
+  blockExplorerUrls: [],
+  defaultRpcEndpointIndex: 0,
+  name: 'Sepolia',
+  nativeCurrency: 'SepoliaETH',
+  rpcEndpoints: [
+    {
+      failoverUrls: [],
+      networkClientId: 'sepolia',
+      type: 'infura',
+      url: 'https://sepolia.infura.io/v3/{infuraProjectId}',
+    },
+  ],
+} as unknown as EIP7702NetworkConfiguration;
+
+describe('Account Network Row', () => {
+  it('renders correctly for smart account', () => {
+    const { getByText } = renderWithProvider(
+      <AccountNetworkRow network={MOCK_NETWORK} />,
+      {},
+    );
+
+    expect(getByText('Smart Account')).toBeTruthy();
+    expect(getByText('Switch')).toBeTruthy();
+  });
+
+  it('renders correctly for standard account', () => {
+    const { getByText } = renderWithProvider(
+      <AccountNetworkRow network={{ ...MOCK_NETWORK, isSupported: false }} />,
+      {},
+    );
+
+    expect(getByText('Standard Account')).toBeTruthy();
+    expect(getByText('Switch')).toBeTruthy();
+  });
+});
diff --git a/app/components/Views/confirmations/components/modals/switch-account-type-modal/account-network-row/account-network-row.tsx b/app/components/Views/confirmations/components/modals/switch-account-type-modal/account-network-row/account-network-row.tsx
new file mode 100644
index 00000000000..bf7ec896a9b
--- /dev/null
+++ b/app/components/Views/confirmations/components/modals/switch-account-type-modal/account-network-row/account-network-row.tsx
@@ -0,0 +1,57 @@
+import React from 'react';
+import { View } from 'react-native';
+
+import { strings } from '../../../../../../../../locales/i18n';
+import Avatar, {
+  AvatarSize,
+  AvatarVariant,
+} from '../../../../../../../component-library/components/Avatars/Avatar';
+import Button, {
+  ButtonVariants,
+} from '../../../../../../../component-library/components/Buttons/Button';
+import Text, {
+  TextColor,
+  TextVariant,
+} from '../../../../../../../component-library/components/Texts/Text';
+import { getNetworkImageSource } from '../../../../../../../util/networks';
+import { useStyles } from '../../../../../../hooks/useStyles';
+import { EIP7702NetworkConfiguration } from '../../../../hooks/useEIP7702Networks';
+import styleSheet from './account-network-row.styles';
+
+const AccountNetworkRow = ({
+  network,
+}: {
+  network: EIP7702NetworkConfiguration;
+}) => {
+  const { styles } = useStyles(styleSheet, {});
+  const { name, chainId, isSupported } = network;
+  const networkImage = getNetworkImageSource({ networkType: 'evm', chainId });
+
+  return (
+    <View style={styles.wrapper}>
+      <View style={styles.left_section}>
+        <Avatar
+          variant={AvatarVariant.Network}
+          size={AvatarSize.Lg}
+          name={name}
+          imageSource={networkImage}
+        />
+        <View style={styles.name_section}>
+          <Text variant={TextVariant.BodyMDBold}>{name}</Text>
+          <Text variant={TextVariant.BodyMD} color={TextColor.Alternative}>
+            {isSupported
+              ? strings('confirm.7702_functionality.smartAccountLabel')
+              : strings('confirm.7702_functionality.standardAccountLabel')}
+          </Text>
+        </View>
+      </View>
+      <Button
+        variant={ButtonVariants.Link}
+        label={strings('confirm.7702_functionality.switch')}
+        onPress={() => undefined}
+      />
+    </View>
+  );
+};
+
+export default AccountNetworkRow;
diff --git a/app/components/Views/confirmations/components/modals/switch-account-type-modal/account-network-row/index.ts b/app/components/Views/confirmations/components/modals/switch-account-type-modal/account-network-row/index.ts
new file mode 100644
index 00000000000..a36f9a82727
--- /dev/null
+++ b/app/components/Views/confirmations/components/modals/switch-account-type-modal/account-network-row/index.ts
@@ -0,0 +1 @@
+export { default } from './account-network-row';
diff --git a/app/components/Views/confirmations/components/modals/switch-account-type-modal/index.ts b/app/components/Views/confirmations/components/modals/switch-account-type-modal/index.ts
new file mode 100644
index 00000000000..324ce4626b7
--- /dev/null
+++ b/app/components/Views/confirmations/components/modals/switch-account-type-modal/index.ts
@@ -0,0 +1 @@
+export { default } from './switch-account-type-modal';
diff --git a/app/components/Views/confirmations/components/modals/switch-account-type-modal/switch-account-type-modal.styles.ts b/app/components/Views/confirmations/components/modals/switch-account-type-modal/switch-account-type-modal.styles.ts
new file mode 100644
index 00000000000..0ab9bcd4cc1
--- /dev/null
+++ b/app/components/Views/confirmations/components/modals/switch-account-type-modal/switch-account-type-modal.styles.ts
@@ -0,0 +1,20 @@
+import { StyleSheet } from 'react-native';
+
+const styleSheet = () =>
+  StyleSheet.create({
+    wrapper: {
+      marginBottom: 16,
+      marginTop: 24,
+    },
+    account_info: {
+      flexDirection: 'column',
+      alignItems: 'center',
+      justifyContent: 'space-between',
+      marginBottom: 24,
+    },
+    account_name: {
+      marginTop: 8,
+    },
+  });
+
+export default styleSheet;
diff --git a/app/components/Views/confirmations/components/modals/switch-account-type-modal/switch-account-type-modal.test.tsx b/app/components/Views/confirmations/components/modals/switch-account-type-modal/switch-account-type-modal.test.tsx
new file mode 100644
index 00000000000..13d4395952d
--- /dev/null
+++ b/app/components/Views/confirmations/components/modals/switch-account-type-modal/switch-account-type-modal.test.tsx
@@ -0,0 +1,101 @@
+import React from 'react';
+
+import renderWithProvider from '../../../../../../util/test/renderWithProvider';
+import { RootState } from '../../../../../../reducers';
+// eslint-disable-next-line import/no-namespace
+import * as Networks7702 from '../../../hooks/useEIP7702Networks';
+import {
+  MOCK_ACCOUNT_CONTROLLER_STATE,
+  MOCK_KEYRING_CONTROLLER_STATE,
+} from '../../../mock-data';
+import { EIP7702NetworkConfiguration } from '../../../hooks/useEIP7702Networks';
+import SwitchAccountTypeModal from './switch-account-type-modal';
+
+const MOCK_NETWORK = {
+  chainId: '0xaa36a7',
+  delegationAddress: '0x63c0c19a282a1b52b07dd5a65b58948a07dae32b',
+  isSupported: true,
+  upgradeContractAddress: '0x63c0c19a282a1B52b07dD5a65b58948A07DAE32B',
+  blockExplorerUrls: [],
+  defaultRpcEndpointIndex: 0,
+  name: 'Sepolia',
+  nativeCurrency: 'SepoliaETH',
+  rpcEndpoints: [
+    {
+      failoverUrls: [],
+      networkClientId: 'sepolia',
+      type: 'infura',
+      url: 'https://sepolia.infura.io/v3/{infuraProjectId}',
+    },
+  ],
+} as unknown as EIP7702NetworkConfiguration;
+
+jest.mock('@react-navigation/native', () => ({
+  ...jest.requireActual('@react-navigation/native'),
+  useRoute: jest.fn().mockReturnValue({
+    params: { address: '0x935e73edb9ff52e23bac7f7e043a1ecd06d05477' },
+  }),
+  useNavigation: () => ({
+    navigate: jest.fn(),
+  }),
+}));
+
+jest.mock('react-native-safe-area-context', () => {
+  const inset = { top: 0, right: 0, bottom: 0, left: 0 };
+  const frame = { width: 0, height: 0, x: 0, y: 0 };
+
+  return {
+    ...jest.requireActual('react-native-safe-area-context'),
+    SafeAreaProvider: jest.fn().mockImplementation(({ children }) => children),
+    SafeAreaConsumer: jest
+      .fn()
+      .mockImplementation(({ children }) => children(inset)),
+    useSafeAreaInsets: jest.fn().mockImplementation(() => inset),
+    useSafeAreaFrame: jest.fn().mockImplementation(() => frame),
+  };
+});
+
+const MOCK_STATE = {
+  engine: {
+    backgroundState: {
+      AccountsController: MOCK_ACCOUNT_CONTROLLER_STATE,
+      KeyringController: MOCK_KEYRING_CONTROLLER_STATE,
+    },
+  },
+} as unknown as RootState;
+
+describe('Switch Account Type Modal', () => {
+  it('displays information correctly', () => {
+    jest.spyOn(Networks7702, 'useEIP7702Networks').mockReturnValue({
+      pending: false,
+      network7702List: [MOCK_NETWORK],
+      networkSupporting7702Present: true,
+    });
+
+    const { getByText } = renderWithProvider(<SwitchAccountTypeModal />, {
+      state: MOCK_STATE,
+    });
+    expect(getByText('Account 1')).toBeTruthy();
+    expect(getByText('Sepolia')).toBeTruthy();
+    expect(getByText('Smart Account')).toBeTruthy();
+    expect(getByText('Switch')).toBeTruthy();
+  });
+
+  it('displays spinner when network list is loading', () => {
+    jest.spyOn(Networks7702, 'useEIP7702Networks').mockReturnValue({
+      pending: true,
+      network7702List: [],
+      networkSupporting7702Present: false,
+    });
+
+    const container = renderWithProvider(<SwitchAccountTypeModal />, {
+      state: MOCK_STATE,
+    });
+    const { getByTestId, queryByText } = container;
+    expect(queryByText('Account 1')).toBeNull();
+    expect(queryByText('Sepolia')).toBeNull();
+    expect(queryByText('Smart Account')).toBeNull();
+    expect(queryByText('Switch')).toBeNull();
+    expect(getByTestId('network-data-loader')).toBeTruthy();
+  });
+});
diff --git a/app/components/Views/confirmations/components/modals/switch-account-type-modal/switch-account-type-modal.tsx b/app/components/Views/confirmations/components/modals/switch-account-type-modal/switch-account-type-modal.tsx
new file mode 100644
index 00000000000..d59a9d311f6
--- /dev/null
+++ b/app/components/Views/confirmations/components/modals/switch-account-type-modal/switch-account-type-modal.tsx
@@ -0,0 +1,61 @@
+import React from 'react';
+import { Hex } from '@metamask/utils';
+import { View } from 'react-native';
+import { useSelector } from 'react-redux';
+import { useRoute } from '@react-navigation/native';
+
+import Avatar, {
+  AvatarSize,
+  AvatarVariant,
+} from '../../../../../../component-library/components/Avatars/Avatar';
+import BottomSheet from '../../../../../../component-library/components/BottomSheets/BottomSheet';
+import Loader from '../../../../../../component-library/components-temp/Loader';
+import Text, {
+  TextVariant,
+} from '../../../../../../component-library/components/Texts/Text';
+import { selectInternalAccounts } from '../../../../../../selectors/accountsController';
+import { useStyles } from '../../../../../hooks/useStyles';
+import { useEIP7702Networks } from '../../../hooks/useEIP7702Networks';
+import AccountNetworkRow from './account-network-row';
+import styleSheet from './switch-account-type-modal.styles';
+
+const SwitchAccountTypeModal = () => {
+  const { styles } = useStyles(styleSheet, {});
+  const route = useRoute();
+  const address = (route?.params as { address: Hex })?.address;
+  const { network7702List, pending } = useEIP7702Networks(address);
+  const internalAccounts = useSelector(selectInternalAccounts);
+  const account = internalAccounts.find(
+    ({ address: accAddress }) => accAddress === address,
+  );
+
+  return (
+    <BottomSheet>
+      <View style={styles.wrapper} testID="network-data-loader">
+        {pending ? (
+          <Loader />
+        ) : (
+          <>
+            <View style={styles.account_info}>
+              <Avatar
+                variant={AvatarVariant.Account}
+                size={AvatarSize.Lg}
+                accountAddress={address}
+              />
+              <Text style={styles.account_name} variant={TextVariant.HeadingMD}>
+                {account?.metadata.name}
+              </Text>
+            </View>
+            <View>
+              {network7702List?.map((networkConfiguration) => (
+                <AccountNetworkRow network={networkConfiguration} />
+              ))}
+            </View>
+          </>
+        )}
+      </View>
+    </BottomSheet>
+  );
+};
+
+export default SwitchAccountTypeModal;
diff --git a/app/components/Views/confirmations/hooks/useEIP7702Networks.test.tsx b/app/components/Views/confirmations/hooks/useEIP7702Networks.test.tsx
new file mode 100644
index 00000000000..0a8ec9f5171
--- /dev/null
+++ b/app/components/Views/confirmations/hooks/useEIP7702Networks.test.tsx
@@ -0,0 +1,79 @@
+import React from 'react';
+import { IsAtomicBatchSupportedResult } from '@metamask/transaction-controller';
+import { View } from 'react-native';
+import { waitFor } from '@testing-library/react-native';
+
+import Text from '../../../../component-library/components/Texts/Text';
+import renderWithProvider, {
+  renderHookWithProvider,
+} from '../../../../util/test/renderWithProvider';
+import { RootState } from '../../../../reducers';
+import {
+  MOCK_MULTICHAIN_NETWORK_CONTROLLER_STATE,
+  MOCK_NETWORK_CONTROLLER_STATE,
+} from '../mock-data';
+import { useEIP7702Networks } from './useEIP7702Networks';
+
+const mockNetworkBatchSupport = [
+  {
+    chainId: '0xaa36a7',
+    delegationAddress: '0x63c0c19a282a1b52b07dd5a65b58948a07dae32b',
+    isSupported: true,
+    upgradeContractAddress: '0x63c0c19a282a1B52b07dD5a65b58948A07DAE32B',
+  },
+] as IsAtomicBatchSupportedResult;
+
+jest.mock('../../../../core/Engine', () => ({
+  context: {
+    TransactionController: {
+      isAtomicBatchSupported: () => Promise.resolve(mockNetworkBatchSupport),
+    },
+  },
+}));
+
+const MOCK_STATE = {
+  engine: {
+    backgroundState: {
+      NetworkController: MOCK_NETWORK_CONTROLLER_STATE,
+      MultichainNetworkController: MOCK_MULTICHAIN_NETWORK_CONTROLLER_STATE,
+    },
+  },
+} as unknown as RootState;
+
+function runHook() {
+  const { result, rerender } = renderHookWithProvider(
+    () => useEIP7702Networks('0x0'),
+    { state: MOCK_STATE },
+  );
+  return { result: result.current, rerender };
+}
+
+const MockComponent = () => {
+  const { network7702List } = useEIP7702Networks('0x0');
+  return (
+    <View>
+      <Text>{`Total networks - ${network7702List?.length}`}</Text>
+      {network7702List?.map(({ name }) => (
+        <Text key={name}>{name}</Text>
+      ))}
+    </View>
+  );
+};
+
+describe('useEIP7702Networks', () => {
+  it('returns pending as true initially', () => {
+    const { result } = runHook();
+    expect(result.pending).toBe(true);
+    expect(result.network7702List).toHaveLength(0);
+  });
+
+  it('returns list of networks', async () => {
+    const { queryByText } = renderWithProvider(<MockComponent />, {
+      state: MOCK_STATE,
+    });
+    await waitFor(() => {
+      expect(queryByText('Total networks - 1')).toBeTruthy();
+      expect(queryByText('Sepolia')).toBeTruthy();
+    });
+  });
+});
diff --git a/app/components/Views/confirmations/hooks/useEIP7702Networks.ts b/app/components/Views/confirmations/hooks/useEIP7702Networks.ts
new file mode 100644
index 00000000000..abe9f5e2034
--- /dev/null
+++ b/app/components/Views/confirmations/hooks/useEIP7702Networks.ts
@@ -0,0 +1,82 @@
+import { Hex } from '@metamask/utils';
+import { MultichainNetworkConfiguration } from '@metamask/multichain-network-controller';
+import { useMemo } from 'react';
+import { useSelector } from 'react-redux';
+
+import Engine from '../../../../core/Engine';
+import { TESTNET_CHAIN_IDS } from '../../../../util/networks';
+import { selectNetworkConfigurations } from '../../../../selectors/networkController';
+import { useAsyncResultOrThrow } from '../../../hooks/useAsyncResult';
+
+export type EIP7702NetworkConfiguration = MultichainNetworkConfiguration & {
+  isSupported: boolean;
+  upgradeContractAddress?: Hex;
+};
+
+export const useEIP7702Networks = (address: string) => {
+  const networks = useSelector(selectNetworkConfigurations);
+
+  const [nonTestNetworks, testNetworks] = useMemo(
+    () =>
+      Object.entries(networks).reduce(
+        ([nonTestnetsList, testnetsList], [chainId, network]) => {
+          try {
+            const isTest = (TESTNET_CHAIN_IDS as string[]).includes(chainId);
+            (isTest ? testnetsList : nonTestnetsList)[chainId] = network;
+          } catch (err: unknown) {
+            // console.log(err);
+          }
+          return [nonTestnetsList, testnetsList];
+        },
+        [
+          {} as Record<string, MultichainNetworkConfiguration>,
+          {} as Record<string, MultichainNetworkConfiguration>,
+        ],
+      ),
+    [networks],
+  );
+  const networkList = useMemo(
+    () => ({ ...nonTestNetworks, ...testNetworks }),
+    [nonTestNetworks, testNetworks],
+  );
+
+  const { pending, value } = useAsyncResultOrThrow(async () => {
+    const chainIds = Object.keys(networkList) as Hex[];
+
+    return await Engine.context.TransactionController.isAtomicBatchSupported({
+      address: address as Hex,
+      chainIds,
+    });
+  }, [address, networkList]);
+
+  const network7702List: EIP7702NetworkConfiguration[] | undefined =
+    useMemo(() => {
+      if (!value) {
+        return [];
+      }
+      const networksSupporting7702: EIP7702NetworkConfiguration[] = [];
+      Object.values(networkList).forEach((network) => {
+        try {
+          const atomicBatchResult = value.find(
+            ({ chainId }) => chainId === network.chainId,
+          );
+          if (atomicBatchResult) {
+            networksSupporting7702.push({
+              ...atomicBatchResult,
+              ...network,
+            });
+          }
+        } catch (err: unknown) {
+          // console.log(err);
+        }
+      });
+
+      return networksSupporting7702;
+    }, [networkList, value]);
+
+  return {
+    network7702List,
+    networkSupporting7702Present: network7702List?.length > 0,
+    pending,
+  };
+};
diff --git a/app/components/Views/confirmations/mock-data.js b/app/components/Views/confirmations/mock-data.js
new file mode 100644
index 00000000000..9321a3419b3
--- /dev/null
+++ b/app/components/Views/confirmations/mock-data.js
@@ -0,0 +1,145 @@
+export const MOCK_NETWORK_CONTROLLER_STATE = {
+  networkConfigurationsByChainId: {
+    '0xaa36a7': {
+      blockExplorerUrls: [],
+      chainId: '0xaa36a7',
+      defaultRpcEndpointIndex: 0,
+      name: 'Sepolia',
+      nativeCurrency: 'SepoliaETH',
+      rpcEndpoints: [
+        {
+          failoverUrls: [],
+          networkClientId: 'sepolia',
+          type: 'infura',
+          url: 'https://sepolia.infura.io/v3/{infuraProjectId}',
+        },
+      ],
+    },
+    '0x18c6': {
+      blockExplorerUrls: ['https://megaexplorer.xyz'],
+      chainId: '0x18c6',
+      defaultRpcEndpointIndex: 0,
+      defaultBlockExplorerUrlIndex: 0,
+      name: 'Mega Testnet',
+      nativeCurrency: 'MegaETH',
+      rpcEndpoints: [
+        {
+          failoverUrls: [],
+          networkClientId: 'megaeth-testnet',
+          type: 'custom',
+          url: 'https://carrot.megaeth.com/rpc',
+        },
+      ],
+    },
+  },
+};
+
+export const MOCK_MULTICHAIN_NETWORK_CONTROLLER_STATE = {
+  isEvmSelected: true,
+  multichainNetworkConfigurationsByChainId: {
+    'bip122:000000000019d6689c085ae165831e93': {
+      chainId: 'bip122:000000000019d6689c085ae165831e93',
+      isEvm: false,
+      name: 'Bitcoin Mainnet',
+      nativeCurrency: 'bip122:000000000019d6689c085ae165831e93/slip44:0',
+    },
+    'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp': {
+      chainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
+      isEvm: false,
+      name: 'Solana Mainnet',
+      nativeCurrency:
+        'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
+    },
+  },
+  selectedMultichainNetworkChainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
+};
+
+export const MOCK_ACCOUNT_CONTROLLER_STATE = {
+  internalAccounts: {
+    accounts: {
+      '94b520b3-a0c9-4cbd-a689-441a01630331': {
+        id: '94b520b3-a0c9-4cbd-a689-441a01630331',
+        address: '0x935e73edb9ff52e23bac7f7e043a1ecd06d05477',
+        options: {},
+        methods: [
+          'personal_sign',
+          'eth_sign',
+          'eth_signTransaction',
+          'eth_signTypedData_v1',
+          'eth_signTypedData_v3',
+          'eth_signTypedData_v4',
+        ],
+        scopes: ['eip155:0'],
+        type: 'eip155:eoa',
+        metadata: {
+          name: 'Account 1',
+          importTime: 1746181774143,
+          keyring: { type: 'HD Key Tree' },
+          lastSelected: 1746191007939,
+        },
+      },
+      '023043ce-6c62-4cab-bf91-8939553a68f2': {
+        id: '023043ce-6c62-4cab-bf91-8939553a68f2',
+        address: '0x089595380921f555d52ab6f5a49defdaab23b444',
+        options: {},
+        methods: [
+          'personal_sign',
+          'eth_sign',
+          'eth_signTransaction',
+          'eth_signTypedData_v1',
+          'eth_signTypedData_v3',
+          'eth_signTypedData_v4',
+        ],
+        scopes: ['eip155:0'],
+        type: 'eip155:eoa',
+        metadata: {
+          name: 'Account 2',
+          importTime: 1746181776738,
+          keyring: { type: 'HD Key Tree' },
+          lastSelected: 0,
+        },
+      },
+      '3fb381cd-76e5-4edb-81f4-b5133b115a8e': {
+        id: '3fb381cd-76e5-4edb-81f4-b5133b115a8e',
+        address: '0xa4a80ce0afdfb8e6bd1221d3b18a1653eee6d19d',
+        options: {},
+        methods: [
+          'personal_sign',
+          'eth_sign',
+          'eth_signTransaction',
+          'eth_signTypedData_v1',
+          'eth_signTypedData_v3',
+          'eth_signTypedData_v4',
+        ],
+        scopes: ['eip155:0'],
+        type: 'eip155:eoa',
+        metadata: {
+          name: 'Account 3',
+          importTime: 1746181779759,
+          keyring: { type: 'HD Key Tree' },
+          lastSelected: 0,
+        },
+      },
+    },
+    selectedAccount: '94b520b3-a0c9-4cbd-a689-441a01630331',
+  },
+};
+
+export const MOCK_KEYRING_CONTROLLER_STATE = {
+  isUnlocked: true,
+  keyrings: [
+    {
+      type: 'HD Key Tree',
+      accounts: [
+        '0x935e73edb9ff52e23bac7f7e043a1ecd06d05477',
+        '0x089595380921f555d52ab6f5a49defdaab23b444',
+        '0xa4a80ce0afdfb8e6bd1221d3b18a1653eee6d19d',
+      ],
+    },
+    { type: 'QR Hardware Wallet Device', accounts: [] },
+  ],
+  keyringsMetadata: [
+    { id: '01JT88PPSFQW1C2SGPKTS874ZX', name: '' },
+    { id: '01JT88PSBXWQ36YBFJWHJAC9T2', name: '' },
+  ],
+};
diff --git a/app/constants/navigation/Routes.ts b/app/constants/navigation/Routes.ts
index ff5e85317f3..d1576586f81 100644
--- a/app/constants/navigation/Routes.ts
+++ b/app/constants/navigation/Routes.ts
@@ -162,6 +162,7 @@ const Routes = {
   LOCK_SCREEN: 'LockScreen',
   CONFIRMATION_REQUEST_FLAT: 'ConfirmationRequestFlat',
   CONFIRMATION_REQUEST_MODAL: 'ConfirmationRequestModal',
+  CONFIRMATION_SWITCH_ACCOUNT_TYPE: 'ConfirmationSwitchAccountType',
   NOTIFICATIONS: {
     VIEW: 'NotificationsView',
     OPT_IN: 'OptIn',
diff --git a/jest.config.js b/jest.config.js
index 3dfde3572b3..572d445061e 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -12,6 +12,8 @@ process.env.SECURITY_ALERTS_API_URL = 'https://example.com';
 process.env.LAUNCH_DARKLY_URL =
   'https://client-config.dev-api.cx.metamask.io/v1';
 
+process.env.MM_SMART_ACCOUNT_UI_ENABLED = 'true';
+
 const config = {
   preset: 'react-native',
   setupFilesAfterEnv: ['<rootDir>/app/util/test/testSetup.js'],
diff --git a/locales/languages/en.json b/locales/languages/en.json
index a0086370f4c..5c38508aba4 100644
--- a/locales/languages/en.json
+++ b/locales/languages/en.json
@@ -3515,6 +3515,7 @@
     "add_bitcoin_account_testnet": "Bitcoin testnet account",
     "add_bitcoin_account_mainnet": "Bitcoin account",
     "add_solana_account": "Solana account",
+    "switch_to_smart_account": "Switch to Smart account",
     "headers": {
       "bitcoin": "Bitcoin",
       "solana": "Solana"
@@ -3867,6 +3868,11 @@
       "tooltip": "Estimated changes are what might happen if you go through with this transaction. This is just a prediction, not a guarantee.",
       "unavailable": "Unavailable"
     },
+    "7702_functionality": {
+      "smartAccountLabel": "Smart Account",
+      "standardAccountLabel": "Standard Account",
+      "switch": "Switch"
+    },
     "unlimited": "Unlimited",
     "none": "None",
     "advanced_details": "Advanced details",


Reviews:
user: login: jpuri
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T12:32:32Z
user: login: OGPoyraz
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-05-05T14:17:57Z
user: login: jpuri
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T14:58:23Z
user: login: jpuri
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T14:59:17Z
user: login: OGPoyraz
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-05T15:06:24Z
user: login: jpuri
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T15:58:49Z
user: login: jpuri
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T16:01:34Z
user: login: jpuri
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T16:01:47Z
user: login: jpuri
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T16:04:08Z
user: login: OGPoyraz
body: Thanks for the fast updates @jpuri !
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-05-05T16:05:22Z
user: login: brianacnguyen
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T21:31:53Z
user: login: brianacnguyen
body: Can you remove the icon?
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T21:32:06Z
user: login: jpuri
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T05:13:37Z
user: login: OGPoyraz
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-05-06T05:37:57Z
user: login: darkwing
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T12:04:34Z
user: login: darkwing
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T12:05:38Z
user: login: darkwing
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T12:06:17Z
user: login: darkwing
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T12:09:11Z
user: login: darkwing
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T12:10:00Z
user: login: jpuri
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T12:15:26Z
user: login: OGPoyraz
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-06T12:15:37Z
user: login: jpuri
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T12:40:16Z
user: login: jpuri
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T12:40:38Z
user: login: jpuri
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T12:41:11Z
user: login: brianacnguyen
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T15:02:59Z

Review comments:
diff_hunk: @@ -170,4 +170,4 @@ describe('NonEvmAggregatedPercentage', () => {
     expect(formattedPercentage.props.children).toBe('••••••••••');
     expect(formattedValuePrice.props.children).toBe('••••••••••');
   });
-});
\ No newline at end of file
+});
path: app/component-library/components-temp/Price/AggregatedPercentage/NonEvmAggregatedPercentage.test.tsx
user: login: jpuri
body: This file only has formatting changes
created_at: 2025-05-05T12:32:32Z
author_association: CONTRIBUTOR
path: app/components/Views/confirmations/components/account-network-row/account-network-row.styles.ts
user: login: OGPoyraz
body: `row` components are reusable in confirmations(either `signature` or `transactions` for now) if this is not reusable in confirmations (meaning if tied to `7702`) let's rename it or remove `row` from name or maybe if we expect that this component will only be used in `switch-account-type-modal` we can even relocate into that folder?
created_at: 2025-05-05T13:47:23Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,114 @@
+import React from 'react';
+// eslint-disable-next-line import/no-namespace
+import * as ReactRedux from 'react-redux';
+
+import renderWithProvider from '../../../../../util/test/renderWithProvider';
+// eslint-disable-next-line import/no-namespace
+import * as Networks7702 from '../../hooks/useEIP7702Networks';
+import { EIP7702NetworkConfiguration } from '../../hooks/useEIP7702Networks';
+import SwitchAccountTypeModal from './switch-account-type-modal';
+
+const MOCK_NETWORK = {
+  chainId: '0xaa36a7',
+  delegationAddress: '0x63c0c19a282a1b52b07dd5a65b58948a07dae32b',
+  isSupported: true,
+  upgradeContractAddress: '0x63c0c19a282a1B52b07dD5a65b58948A07DAE32B',
+  blockExplorerUrls: [],
+  defaultRpcEndpointIndex: 0,
+  name: 'Sepolia',
+  nativeCurrency: 'SepoliaETH',
+  rpcEndpoints: [
+    {
+      failoverUrls: [],
+      networkClientId: 'sepolia',
+      type: 'infura',
+      url: 'https://sepolia.infura.io/v3/{infuraProjectId}',
+    },
+  ],
+} as unknown as EIP7702NetworkConfiguration;
+
+const MOCK_ACCOUNT = {
+  id: '94b520b3-a0c9-4cbd-a689-441a01630331',
+  address: '0x935e73edb9ff52e23bac7f7e043a1ecd06d05477',
+  options: {},
+  methods: [
+    'personal_sign',
+    'eth_sign',
+    'eth_signTransaction',
+    'eth_signTypedData_v1',
+    'eth_signTypedData_v3',
+    'eth_signTypedData_v4',
+  ],
+  scopes: ['eip155:0'],
+  type: 'eip155:eoa',
+  metadata: {
+    name: 'Account 1',
+    importTime: 1746181774143,
+    keyring: { type: 'HD Key Tree' },
+    lastSelected: 1746191007939,
+  },
+};
+
+jest.mock('react-redux', () => ({
+  ...jest.requireActual('react-redux'),
+  useSelector: jest.fn(),
+}));
+
+jest.mock('@react-navigation/native', () => ({
+  ...jest.requireActual('@react-navigation/native'),
+  useRoute: jest.fn().mockReturnValue({
+    params: { address: '0x935e73edb9ff52e23bac7f7e043a1ecd06d05477' },
+  }),
+  useNavigation: () => ({
+    navigate: jest.fn(),
+  }),
+}));
+
+jest.mock('react-native-safe-area-context', () => {
+  const inset = { top: 0, right: 0, bottom: 0, left: 0 };
+  const frame = { width: 0, height: 0, x: 0, y: 0 };
+
+  return {
+    ...jest.requireActual('react-native-safe-area-context'),
+    SafeAreaProvider: jest.fn().mockImplementation(({ children }) => children),
+    SafeAreaConsumer: jest
+      .fn()
+      .mockImplementation(({ children }) => children(inset)),
+    useSafeAreaInsets: jest.fn().mockImplementation(() => inset),
+    useSafeAreaFrame: jest.fn().mockImplementation(() => frame),
+  };
+});
+
+describe('Switch Account Type Modal', () => {
+  it('displays information correctly', () => {
+    jest.spyOn(Networks7702, 'useEIP7702Networks').mockReturnValue({
+      pending: false,
+      network7702List: [MOCK_NETWORK],
+      networkSupporting7702Present: true,
+    });
+    jest.spyOn(ReactRedux, 'useSelector').mockReturnValue([MOCK_ACCOUNT]);
+
+    const { getByText } = renderWithProvider(<SwitchAccountTypeModal />, {});
+    expect(getByText('Account 1')).toBeTruthy();
+    expect(getByText('Sepolia')).toBeTruthy();
+    expect(getByText('Smart Account')).toBeTruthy();
+    expect(getByText('Switch')).toBeTruthy();
+  });
+
+  it('displays spinner when network list is loading', () => {
+    jest.spyOn(Networks7702, 'useEIP7702Networks').mockReturnValue({
+      pending: true,
+      network7702List: [],
+      networkSupporting7702Present: false,
+    });
+    jest.spyOn(ReactRedux, 'useSelector').mockReturnValue([MOCK_ACCOUNT]);
+
+    const container = renderWithProvider(<SwitchAccountTypeModal />, {});
+    const { queryByText } = container;
+    expect(queryByText('Account 1')).toBeNull();
+    expect(queryByText('Sepolia')).toBeNull();
+    expect(queryByText('Smart Account')).toBeNull();
+    expect(queryByText('Switch')).toBeNull();
+    expect(container).toMatchSnapshot();
path: app/components/Views/confirmations/components/switch-account-type-modal/switch-account-type-modal.test.tsx
user: login: OGPoyraz
body: I remember we don't want to have snapshots in the tests, any reason that we have them here? 
created_at: 2025-05-05T13:49:45Z
author_association: MEMBER
path: app/components/Views/confirmations/components/switch-account-type-modal/index.ts
user: login: OGPoyraz
body: Can we relocate this into `modals` directory?
created_at: 2025-05-05T13:50:07Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,114 @@
+import React from 'react';
+// eslint-disable-next-line import/no-namespace
+import * as ReactRedux from 'react-redux';
+
+import renderWithProvider from '../../../../../util/test/renderWithProvider';
+// eslint-disable-next-line import/no-namespace
+import * as Networks7702 from '../../hooks/useEIP7702Networks';
+import { EIP7702NetworkConfiguration } from '../../hooks/useEIP7702Networks';
+import SwitchAccountTypeModal from './switch-account-type-modal';
+
+const MOCK_NETWORK = {
+  chainId: '0xaa36a7',
+  delegationAddress: '0x63c0c19a282a1b52b07dd5a65b58948a07dae32b',
+  isSupported: true,
+  upgradeContractAddress: '0x63c0c19a282a1B52b07dD5a65b58948A07DAE32B',
+  blockExplorerUrls: [],
+  defaultRpcEndpointIndex: 0,
+  name: 'Sepolia',
+  nativeCurrency: 'SepoliaETH',
+  rpcEndpoints: [
+    {
+      failoverUrls: [],
+      networkClientId: 'sepolia',
+      type: 'infura',
+      url: 'https://sepolia.infura.io/v3/{infuraProjectId}',
+    },
+  ],
+} as unknown as EIP7702NetworkConfiguration;
+
+const MOCK_ACCOUNT = {
+  id: '94b520b3-a0c9-4cbd-a689-441a01630331',
+  address: '0x935e73edb9ff52e23bac7f7e043a1ecd06d05477',
+  options: {},
+  methods: [
+    'personal_sign',
+    'eth_sign',
+    'eth_signTransaction',
+    'eth_signTypedData_v1',
+    'eth_signTypedData_v3',
+    'eth_signTypedData_v4',
+  ],
+  scopes: ['eip155:0'],
+  type: 'eip155:eoa',
+  metadata: {
+    name: 'Account 1',
+    importTime: 1746181774143,
+    keyring: { type: 'HD Key Tree' },
+    lastSelected: 1746191007939,
+  },
+};
path: app/components/Views/confirmations/components/switch-account-type-modal/switch-account-type-modal.test.tsx
user: login: OGPoyraz
body: Hopefully we can move them into `mock-data` folder and remove selectors mocks as we agreed on, to not block this PR as this is first one for 7702 - hoping we could resolve this on future PRs.

created_at: 2025-05-05T14:14:44Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,114 @@
+import React from 'react';
+// eslint-disable-next-line import/no-namespace
+import * as ReactRedux from 'react-redux';
+
+import renderWithProvider from '../../../../../util/test/renderWithProvider';
+// eslint-disable-next-line import/no-namespace
+import * as Networks7702 from '../../hooks/useEIP7702Networks';
+import { EIP7702NetworkConfiguration } from '../../hooks/useEIP7702Networks';
+import SwitchAccountTypeModal from './switch-account-type-modal';
+
+const MOCK_NETWORK = {
+  chainId: '0xaa36a7',
+  delegationAddress: '0x63c0c19a282a1b52b07dd5a65b58948a07dae32b',
+  isSupported: true,
+  upgradeContractAddress: '0x63c0c19a282a1B52b07dD5a65b58948A07DAE32B',
+  blockExplorerUrls: [],
+  defaultRpcEndpointIndex: 0,
+  name: 'Sepolia',
+  nativeCurrency: 'SepoliaETH',
+  rpcEndpoints: [
+    {
+      failoverUrls: [],
+      networkClientId: 'sepolia',
+      type: 'infura',
+      url: 'https://sepolia.infura.io/v3/{infuraProjectId}',
+    },
+  ],
+} as unknown as EIP7702NetworkConfiguration;
+
+const MOCK_ACCOUNT = {
+  id: '94b520b3-a0c9-4cbd-a689-441a01630331',
+  address: '0x935e73edb9ff52e23bac7f7e043a1ecd06d05477',
+  options: {},
+  methods: [
+    'personal_sign',
+    'eth_sign',
+    'eth_signTransaction',
+    'eth_signTypedData_v1',
+    'eth_signTypedData_v3',
+    'eth_signTypedData_v4',
+  ],
+  scopes: ['eip155:0'],
+  type: 'eip155:eoa',
+  metadata: {
+    name: 'Account 1',
+    importTime: 1746181774143,
+    keyring: { type: 'HD Key Tree' },
+    lastSelected: 1746191007939,
+  },
+};
+
+jest.mock('react-redux', () => ({
+  ...jest.requireActual('react-redux'),
+  useSelector: jest.fn(),
+}));
+
+jest.mock('@react-navigation/native', () => ({
+  ...jest.requireActual('@react-navigation/native'),
+  useRoute: jest.fn().mockReturnValue({
+    params: { address: '0x935e73edb9ff52e23bac7f7e043a1ecd06d05477' },
+  }),
+  useNavigation: () => ({
+    navigate: jest.fn(),
+  }),
+}));
+
+jest.mock('react-native-safe-area-context', () => {
+  const inset = { top: 0, right: 0, bottom: 0, left: 0 };
+  const frame = { width: 0, height: 0, x: 0, y: 0 };
+
+  return {
+    ...jest.requireActual('react-native-safe-area-context'),
+    SafeAreaProvider: jest.fn().mockImplementation(({ children }) => children),
+    SafeAreaConsumer: jest
+      .fn()
+      .mockImplementation(({ children }) => children(inset)),
+    useSafeAreaInsets: jest.fn().mockImplementation(() => inset),
+    useSafeAreaFrame: jest.fn().mockImplementation(() => frame),
+  };
+});
+
+describe('Switch Account Type Modal', () => {
+  it('displays information correctly', () => {
+    jest.spyOn(Networks7702, 'useEIP7702Networks').mockReturnValue({
+      pending: false,
+      network7702List: [MOCK_NETWORK],
+      networkSupporting7702Present: true,
+    });
+    jest.spyOn(ReactRedux, 'useSelector').mockReturnValue([MOCK_ACCOUNT]);
+
+    const { getByText } = renderWithProvider(<SwitchAccountTypeModal />, {});
+    expect(getByText('Account 1')).toBeTruthy();
+    expect(getByText('Sepolia')).toBeTruthy();
+    expect(getByText('Smart Account')).toBeTruthy();
+    expect(getByText('Switch')).toBeTruthy();
+  });
+
+  it('displays spinner when network list is loading', () => {
+    jest.spyOn(Networks7702, 'useEIP7702Networks').mockReturnValue({
+      pending: true,
+      network7702List: [],
+      networkSupporting7702Present: false,
+    });
+    jest.spyOn(ReactRedux, 'useSelector').mockReturnValue([MOCK_ACCOUNT]);
+
+    const container = renderWithProvider(<SwitchAccountTypeModal />, {});
+    const { queryByText } = container;
+    expect(queryByText('Account 1')).toBeNull();
+    expect(queryByText('Sepolia')).toBeNull();
+    expect(queryByText('Smart Account')).toBeNull();
+    expect(queryByText('Switch')).toBeNull();
+    expect(container).toMatchSnapshot();
path: app/components/Views/confirmations/components/switch-account-type-modal/switch-account-type-modal.test.tsx
user: login: jpuri
body: I added for this one case along with `queryByText` as this is scenario a spinner is shown and it better to check with snapshot.
created_at: 2025-05-05T14:58:23Z
author_association: CONTRIBUTOR
path: app/components/Views/confirmations/components/account-network-row/account-network-row.styles.ts
user: login: jpuri
body: yep relocating to the folder will be better, let me do that
created_at: 2025-05-05T14:59:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,114 @@
+import React from 'react';
+// eslint-disable-next-line import/no-namespace
+import * as ReactRedux from 'react-redux';
+
+import renderWithProvider from '../../../../../util/test/renderWithProvider';
+// eslint-disable-next-line import/no-namespace
+import * as Networks7702 from '../../hooks/useEIP7702Networks';
+import { EIP7702NetworkConfiguration } from '../../hooks/useEIP7702Networks';
+import SwitchAccountTypeModal from './switch-account-type-modal';
+
+const MOCK_NETWORK = {
+  chainId: '0xaa36a7',
+  delegationAddress: '0x63c0c19a282a1b52b07dd5a65b58948a07dae32b',
+  isSupported: true,
+  upgradeContractAddress: '0x63c0c19a282a1B52b07dD5a65b58948A07DAE32B',
+  blockExplorerUrls: [],
+  defaultRpcEndpointIndex: 0,
+  name: 'Sepolia',
+  nativeCurrency: 'SepoliaETH',
+  rpcEndpoints: [
+    {
+      failoverUrls: [],
+      networkClientId: 'sepolia',
+      type: 'infura',
+      url: 'https://sepolia.infura.io/v3/{infuraProjectId}',
+    },
+  ],
+} as unknown as EIP7702NetworkConfiguration;
+
+const MOCK_ACCOUNT = {
+  id: '94b520b3-a0c9-4cbd-a689-441a01630331',
+  address: '0x935e73edb9ff52e23bac7f7e043a1ecd06d05477',
+  options: {},
+  methods: [
+    'personal_sign',
+    'eth_sign',
+    'eth_signTransaction',
+    'eth_signTypedData_v1',
+    'eth_signTypedData_v3',
+    'eth_signTypedData_v4',
+  ],
+  scopes: ['eip155:0'],
+  type: 'eip155:eoa',
+  metadata: {
+    name: 'Account 1',
+    importTime: 1746181774143,
+    keyring: { type: 'HD Key Tree' },
+    lastSelected: 1746191007939,
+  },
+};
+
+jest.mock('react-redux', () => ({
+  ...jest.requireActual('react-redux'),
+  useSelector: jest.fn(),
+}));
+
+jest.mock('@react-navigation/native', () => ({
+  ...jest.requireActual('@react-navigation/native'),
+  useRoute: jest.fn().mockReturnValue({
+    params: { address: '0x935e73edb9ff52e23bac7f7e043a1ecd06d05477' },
+  }),
+  useNavigation: () => ({
+    navigate: jest.fn(),
+  }),
+}));
+
+jest.mock('react-native-safe-area-context', () => {
+  const inset = { top: 0, right: 0, bottom: 0, left: 0 };
+  const frame = { width: 0, height: 0, x: 0, y: 0 };
+
+  return {
+    ...jest.requireActual('react-native-safe-area-context'),
+    SafeAreaProvider: jest.fn().mockImplementation(({ children }) => children),
+    SafeAreaConsumer: jest
+      .fn()
+      .mockImplementation(({ children }) => children(inset)),
+    useSafeAreaInsets: jest.fn().mockImplementation(() => inset),
+    useSafeAreaFrame: jest.fn().mockImplementation(() => frame),
+  };
+});
+
+describe('Switch Account Type Modal', () => {
+  it('displays information correctly', () => {
+    jest.spyOn(Networks7702, 'useEIP7702Networks').mockReturnValue({
+      pending: false,
+      network7702List: [MOCK_NETWORK],
+      networkSupporting7702Present: true,
+    });
+    jest.spyOn(ReactRedux, 'useSelector').mockReturnValue([MOCK_ACCOUNT]);
+
+    const { getByText } = renderWithProvider(<SwitchAccountTypeModal />, {});
+    expect(getByText('Account 1')).toBeTruthy();
+    expect(getByText('Sepolia')).toBeTruthy();
+    expect(getByText('Smart Account')).toBeTruthy();
+    expect(getByText('Switch')).toBeTruthy();
+  });
+
+  it('displays spinner when network list is loading', () => {
+    jest.spyOn(Networks7702, 'useEIP7702Networks').mockReturnValue({
+      pending: true,
+      network7702List: [],
+      networkSupporting7702Present: false,
+    });
+    jest.spyOn(ReactRedux, 'useSelector').mockReturnValue([MOCK_ACCOUNT]);
+
+    const container = renderWithProvider(<SwitchAccountTypeModal />, {});
+    const { queryByText } = container;
+    expect(queryByText('Account 1')).toBeNull();
+    expect(queryByText('Sepolia')).toBeNull();
+    expect(queryByText('Smart Account')).toBeNull();
+    expect(queryByText('Switch')).toBeNull();
+    expect(container).toMatchSnapshot();
path: app/components/Views/confirmations/components/switch-account-type-modal/switch-account-type-modal.test.tsx
user: login: OGPoyraz
body: Okay it make sense
created_at: 2025-05-05T15:06:24Z
author_association: MEMBER
path: app/components/Views/confirmations/components/switch-account-type-modal/index.ts
user: login: jpuri
body: PR is updated
created_at: 2025-05-05T15:58:49Z
author_association: CONTRIBUTOR
path: app/components/Views/confirmations/components/account-network-row/account-network-row.styles.ts
user: login: jpuri
body: PR is updated
created_at: 2025-05-05T16:01:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,114 @@
+import React from 'react';
+// eslint-disable-next-line import/no-namespace
+import * as ReactRedux from 'react-redux';
+
+import renderWithProvider from '../../../../../util/test/renderWithProvider';
+// eslint-disable-next-line import/no-namespace
+import * as Networks7702 from '../../hooks/useEIP7702Networks';
+import { EIP7702NetworkConfiguration } from '../../hooks/useEIP7702Networks';
+import SwitchAccountTypeModal from './switch-account-type-modal';
+
+const MOCK_NETWORK = {
+  chainId: '0xaa36a7',
+  delegationAddress: '0x63c0c19a282a1b52b07dd5a65b58948a07dae32b',
+  isSupported: true,
+  upgradeContractAddress: '0x63c0c19a282a1B52b07dD5a65b58948A07DAE32B',
+  blockExplorerUrls: [],
+  defaultRpcEndpointIndex: 0,
+  name: 'Sepolia',
+  nativeCurrency: 'SepoliaETH',
+  rpcEndpoints: [
+    {
+      failoverUrls: [],
+      networkClientId: 'sepolia',
+      type: 'infura',
+      url: 'https://sepolia.infura.io/v3/{infuraProjectId}',
+    },
+  ],
+} as unknown as EIP7702NetworkConfiguration;
+
+const MOCK_ACCOUNT = {
+  id: '94b520b3-a0c9-4cbd-a689-441a01630331',
+  address: '0x935e73edb9ff52e23bac7f7e043a1ecd06d05477',
+  options: {},
+  methods: [
+    'personal_sign',
+    'eth_sign',
+    'eth_signTransaction',
+    'eth_signTypedData_v1',
+    'eth_signTypedData_v3',
+    'eth_signTypedData_v4',
+  ],
+  scopes: ['eip155:0'],
+  type: 'eip155:eoa',
+  metadata: {
+    name: 'Account 1',
+    importTime: 1746181774143,
+    keyring: { type: 'HD Key Tree' },
+    lastSelected: 1746191007939,
+  },
+};
path: app/components/Views/confirmations/components/switch-account-type-modal/switch-account-type-modal.test.tsx
user: login: jpuri
body: PR is updated
created_at: 2025-05-05T16:01:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,114 @@
+import React from 'react';
+// eslint-disable-next-line import/no-namespace
+import * as ReactRedux from 'react-redux';
+
+import renderWithProvider from '../../../../../util/test/renderWithProvider';
+// eslint-disable-next-line import/no-namespace
+import * as Networks7702 from '../../hooks/useEIP7702Networks';
+import { EIP7702NetworkConfiguration } from '../../hooks/useEIP7702Networks';
+import SwitchAccountTypeModal from './switch-account-type-modal';
+
+const MOCK_NETWORK = {
+  chainId: '0xaa36a7',
+  delegationAddress: '0x63c0c19a282a1b52b07dd5a65b58948a07dae32b',
+  isSupported: true,
+  upgradeContractAddress: '0x63c0c19a282a1B52b07dD5a65b58948A07DAE32B',
+  blockExplorerUrls: [],
+  defaultRpcEndpointIndex: 0,
+  name: 'Sepolia',
+  nativeCurrency: 'SepoliaETH',
+  rpcEndpoints: [
+    {
+      failoverUrls: [],
+      networkClientId: 'sepolia',
+      type: 'infura',
+      url: 'https://sepolia.infura.io/v3/{infuraProjectId}',
+    },
+  ],
+} as unknown as EIP7702NetworkConfiguration;
+
+const MOCK_ACCOUNT = {
+  id: '94b520b3-a0c9-4cbd-a689-441a01630331',
+  address: '0x935e73edb9ff52e23bac7f7e043a1ecd06d05477',
+  options: {},
+  methods: [
+    'personal_sign',
+    'eth_sign',
+    'eth_signTransaction',
+    'eth_signTypedData_v1',
+    'eth_signTypedData_v3',
+    'eth_signTypedData_v4',
+  ],
+  scopes: ['eip155:0'],
+  type: 'eip155:eoa',
+  metadata: {
+    name: 'Account 1',
+    importTime: 1746181774143,
+    keyring: { type: 'HD Key Tree' },
+    lastSelected: 1746191007939,
+  },
+};
+
+jest.mock('react-redux', () => ({
+  ...jest.requireActual('react-redux'),
+  useSelector: jest.fn(),
+}));
+
+jest.mock('@react-navigation/native', () => ({
+  ...jest.requireActual('@react-navigation/native'),
+  useRoute: jest.fn().mockReturnValue({
+    params: { address: '0x935e73edb9ff52e23bac7f7e043a1ecd06d05477' },
+  }),
+  useNavigation: () => ({
+    navigate: jest.fn(),
+  }),
+}));
+
+jest.mock('react-native-safe-area-context', () => {
+  const inset = { top: 0, right: 0, bottom: 0, left: 0 };
+  const frame = { width: 0, height: 0, x: 0, y: 0 };
+
+  return {
+    ...jest.requireActual('react-native-safe-area-context'),
+    SafeAreaProvider: jest.fn().mockImplementation(({ children }) => children),
+    SafeAreaConsumer: jest
+      .fn()
+      .mockImplementation(({ children }) => children(inset)),
+    useSafeAreaInsets: jest.fn().mockImplementation(() => inset),
+    useSafeAreaFrame: jest.fn().mockImplementation(() => frame),
+  };
+});
+
+describe('Switch Account Type Modal', () => {
+  it('displays information correctly', () => {
+    jest.spyOn(Networks7702, 'useEIP7702Networks').mockReturnValue({
+      pending: false,
+      network7702List: [MOCK_NETWORK],
+      networkSupporting7702Present: true,
+    });
+    jest.spyOn(ReactRedux, 'useSelector').mockReturnValue([MOCK_ACCOUNT]);
+
+    const { getByText } = renderWithProvider(<SwitchAccountTypeModal />, {});
+    expect(getByText('Account 1')).toBeTruthy();
+    expect(getByText('Sepolia')).toBeTruthy();
+    expect(getByText('Smart Account')).toBeTruthy();
+    expect(getByText('Switch')).toBeTruthy();
+  });
+
+  it('displays spinner when network list is loading', () => {
+    jest.spyOn(Networks7702, 'useEIP7702Networks').mockReturnValue({
+      pending: true,
+      network7702List: [],
+      networkSupporting7702Present: false,
+    });
+    jest.spyOn(ReactRedux, 'useSelector').mockReturnValue([MOCK_ACCOUNT]);
+
+    const container = renderWithProvider(<SwitchAccountTypeModal />, {});
+    const { queryByText } = container;
+    expect(queryByText('Account 1')).toBeNull();
+    expect(queryByText('Sepolia')).toBeNull();
+    expect(queryByText('Smart Account')).toBeNull();
+    expect(queryByText('Switch')).toBeNull();
+    expect(container).toMatchSnapshot();
path: app/components/Views/confirmations/components/switch-account-type-modal/switch-account-type-modal.test.tsx
user: login: jpuri
body: I replaced it with data-testid
created_at: 2025-05-05T16:04:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -86,6 +86,7 @@ export enum IconName {
   ArrowDoubleRight = 'ArrowDoubleRight',
   ArrowDown = 'ArrowDown',
   ArrowLeft = 'ArrowLeft',
+  ArrowOpposite = 'ArrowOpposite',
path: app/component-library/components/Icons/Icon/Icon.types.ts
user: login: brianacnguyen
body: Can you remove this change? There is an icon refresh effort right now, and this icon is part of it (This is called Swap horizontal in the new icon set
https://github.com/MetaMask/metamask-mobile/pull/14733 
created_at: 2025-05-05T21:31:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -86,6 +86,7 @@ export enum IconName {
   ArrowDoubleRight = 'ArrowDoubleRight',
   ArrowDown = 'ArrowDown',
   ArrowLeft = 'ArrowLeft',
+  ArrowOpposite = 'ArrowOpposite',
path: app/component-library/components/Icons/Icon/Icon.types.ts
user: login: jpuri
body: Hey @brianacnguyen : PR is updated.
created_at: 2025-05-06T05:13:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -703,6 +719,10 @@ const AppFlow = () => {
         name={Routes.CONFIRMATION_REQUEST_MODAL}
         component={ModalConfirmationRequest}
       />
+      <Stack.Screen
path: app/components/Nav/App/App.tsx
user: login: darkwing
body: Should the be behind `MM_SMART_ACCOUNT_UI_ENABLED` flag?  It would invoke one less stack if not needed.
created_at: 2025-05-06T12:04:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,57 @@
+import React from 'react';
+import { View } from 'react-native';
+
+import { strings } from '../../../../../../../../locales/i18n';
+import Avatar, {
+  AvatarSize,
+  AvatarVariant,
+} from '../../../../../../../component-library/components/Avatars/Avatar';
+import Button, {
+  ButtonVariants,
+} from '../../../../../../../component-library/components/Buttons/Button';
+import Text, {
+  TextColor,
+  TextVariant,
+} from '../../../../../../../component-library/components/Texts/Text';
+import { getNetworkImageSource } from '../../../../../../../util/networks';
+import { useStyles } from '../../../../../../hooks/useStyles';
+import { EIP7702NetworkConfiguration } from '../../../../hooks/useEIP7702Networks';
+import styleSheet from './account-network-row.styles';
+
+const AccountNetworkRow = ({
+  network,
+}: {
+  network: EIP7702NetworkConfiguration;
+}) => {
+  const { styles } = useStyles(styleSheet, {});
+  const { name, chainId, isSupported } = network;
+  const networkImage = getNetworkImageSource({ networkType: 'evm', chainId });
+
+  return (
+    <View style={styles.wrapper}>
+      <View style={styles.left_section}>
+        <Avatar
+          variant={AvatarVariant.Network}
+          size={AvatarSize.Lg}
+          name={name}
+          imageSource={networkImage}
+        />
+        <View style={styles.name_section}>
+          <Text variant={TextVariant.BodyMDBold}>{name}</Text>
+          <Text variant={TextVariant.BodyMD} color={TextColor.Alternative}>
+            {isSupported
+              ? strings('confirm.7702_functionality.smartAccountLabel')
+              : strings('confirm.7702_functionality.standardAccountLabel')}
+          </Text>
+        </View>
+      </View>
+      <Button
+        variant={ButtonVariants.Link}
+        label={strings('confirm.7702_functionality.switch')}
+        onPress={() => undefined}
path: app/components/Views/confirmations/components/modals/switch-account-type-modal/account-network-row/account-network-row.tsx
user: login: darkwing
body: I know this is behind flag so this callback works for now.
created_at: 2025-05-06T12:05:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,61 @@
+import React from 'react';
+import { Hex } from '@metamask/utils';
+import { View } from 'react-native';
+import { useSelector } from 'react-redux';
+import { useRoute } from '@react-navigation/native';
+
+import Avatar, {
+  AvatarSize,
+  AvatarVariant,
+} from '../../../../../../component-library/components/Avatars/Avatar';
+import BottomSheet from '../../../../../../component-library/components/BottomSheets/BottomSheet';
+import Loader from '../../../../../../component-library/components-temp/Loader';
+import Text, {
+  TextVariant,
+} from '../../../../../../component-library/components/Texts/Text';
+import { selectInternalAccounts } from '../../../../../../selectors/accountsController';
+import { useStyles } from '../../../../../hooks/useStyles';
+import { useEIP7702Networks } from '../../../hooks/useEIP7702Networks';
+import AccountNetworkRow from './account-network-row';
+import styleSheet from './switch-account-type-modal.styles';
+
+const SwitchAccountTypeModal = () => {
+  const { styles } = useStyles(styleSheet, {});
+  const route = useRoute();
+  const address = (route?.params as { address: Hex })?.address;
+  const { network7702List, pending } = useEIP7702Networks(address);
+  const internalAccounts = useSelector(selectInternalAccounts);
+  const account = internalAccounts.find(
path: app/components/Views/confirmations/components/modals/switch-account-type-modal/switch-account-type-modal.tsx
user: login: darkwing
body: We probably want a `useMemo` for this.
created_at: 2025-05-06T12:06:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,82 @@
+import { Hex } from '@metamask/utils';
+import { MultichainNetworkConfiguration } from '@metamask/multichain-network-controller';
+import { useMemo } from 'react';
+import { useSelector } from 'react-redux';
+
+import Engine from '../../../../core/Engine';
+import { TESTNET_CHAIN_IDS } from '../../../../util/networks';
+import { selectNetworkConfigurations } from '../../../../selectors/networkController';
+import { useAsyncResultOrThrow } from '../../../hooks/useAsyncResult';
+
+export type EIP7702NetworkConfiguration = MultichainNetworkConfiguration & {
+  isSupported: boolean;
+  upgradeContractAddress?: Hex;
+};
+
+export const useEIP7702Networks = (address: string) => {
+  const networks = useSelector(selectNetworkConfigurations);
+
+  const [nonTestNetworks, testNetworks] = useMemo(
+    () =>
+      Object.entries(networks).reduce(
+        ([nonTestnetsList, testnetsList], [chainId, network]) => {
+          try {
path: app/components/Views/confirmations/hooks/useEIP7702Networks.ts
user: login: darkwing
body: Is the try/catch required?  Seems like some basic data management.  Also, should we add two selectors like "selectTestNetworkConfigurations" and "selectNonTestNetworkConfigruations"?
created_at: 2025-05-06T12:09:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,57 @@
+import React from 'react';
+import { View } from 'react-native';
+
+import { strings } from '../../../../../../../../locales/i18n';
+import Avatar, {
+  AvatarSize,
+  AvatarVariant,
+} from '../../../../../../../component-library/components/Avatars/Avatar';
+import Button, {
+  ButtonVariants,
+} from '../../../../../../../component-library/components/Buttons/Button';
+import Text, {
+  TextColor,
+  TextVariant,
+} from '../../../../../../../component-library/components/Texts/Text';
+import { getNetworkImageSource } from '../../../../../../../util/networks';
+import { useStyles } from '../../../../../../hooks/useStyles';
+import { EIP7702NetworkConfiguration } from '../../../../hooks/useEIP7702Networks';
+import styleSheet from './account-network-row.styles';
+
+const AccountNetworkRow = ({
+  network,
+}: {
+  network: EIP7702NetworkConfiguration;
+}) => {
+  const { styles } = useStyles(styleSheet, {});
+  const { name, chainId, isSupported } = network;
+  const networkImage = getNetworkImageSource({ networkType: 'evm', chainId });
+
+  return (
+    <View style={styles.wrapper}>
+      <View style={styles.left_section}>
+        <Avatar
+          variant={AvatarVariant.Network}
+          size={AvatarSize.Lg}
+          name={name}
+          imageSource={networkImage}
+        />
+        <View style={styles.name_section}>
+          <Text variant={TextVariant.BodyMDBold}>{name}</Text>
+          <Text variant={TextVariant.BodyMD} color={TextColor.Alternative}>
+            {isSupported
+              ? strings('confirm.7702_functionality.smartAccountLabel')
+              : strings('confirm.7702_functionality.standardAccountLabel')}
+          </Text>
+        </View>
+      </View>
+      <Button
+        variant={ButtonVariants.Link}
+        label={strings('confirm.7702_functionality.switch')}
+        onPress={() => undefined}
path: app/components/Views/confirmations/components/modals/switch-account-type-modal/account-network-row/account-network-row.tsx
user: login: jpuri
body: yep, I am implementing it in a following PR
created_at: 2025-05-06T12:15:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,61 @@
+import React from 'react';
+import { Hex } from '@metamask/utils';
+import { View } from 'react-native';
+import { useSelector } from 'react-redux';
+import { useRoute } from '@react-navigation/native';
+
+import Avatar, {
+  AvatarSize,
+  AvatarVariant,
+} from '../../../../../../component-library/components/Avatars/Avatar';
+import BottomSheet from '../../../../../../component-library/components/BottomSheets/BottomSheet';
+import Loader from '../../../../../../component-library/components-temp/Loader';
+import Text, {
+  TextVariant,
+} from '../../../../../../component-library/components/Texts/Text';
+import { selectInternalAccounts } from '../../../../../../selectors/accountsController';
+import { useStyles } from '../../../../../hooks/useStyles';
+import { useEIP7702Networks } from '../../../hooks/useEIP7702Networks';
+import AccountNetworkRow from './account-network-row';
+import styleSheet from './switch-account-type-modal.styles';
+
+const SwitchAccountTypeModal = () => {
+  const { styles } = useStyles(styleSheet, {});
+  const route = useRoute();
+  const address = (route?.params as { address: Hex })?.address;
+  const { network7702List, pending } = useEIP7702Networks(address);
+  const internalAccounts = useSelector(selectInternalAccounts);
+  const account = internalAccounts.find(
path: app/components/Views/confirmations/components/modals/switch-account-type-modal/switch-account-type-modal.tsx
user: login: jpuri
body: This is not very heavy processing, may be we can avoid `useMemo`.
created_at: 2025-05-06T12:40:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -703,6 +719,10 @@ const AppFlow = () => {
         name={Routes.CONFIRMATION_REQUEST_MODAL}
         component={ModalConfirmationRequest}
       />
+      <Stack.Screen
path: app/components/Nav/App/App.tsx
user: login: jpuri
body: I am fixing this in following PR: https://github.com/MetaMask/metamask-mobile/pull/15158
created_at: 2025-05-06T12:40:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,82 @@
+import { Hex } from '@metamask/utils';
+import { MultichainNetworkConfiguration } from '@metamask/multichain-network-controller';
+import { useMemo } from 'react';
+import { useSelector } from 'react-redux';
+
+import Engine from '../../../../core/Engine';
+import { TESTNET_CHAIN_IDS } from '../../../../util/networks';
+import { selectNetworkConfigurations } from '../../../../selectors/networkController';
+import { useAsyncResultOrThrow } from '../../../hooks/useAsyncResult';
+
+export type EIP7702NetworkConfiguration = MultichainNetworkConfiguration & {
+  isSupported: boolean;
+  upgradeContractAddress?: Hex;
+};
+
+export const useEIP7702Networks = (address: string) => {
+  const networks = useSelector(selectNetworkConfigurations);
+
+  const [nonTestNetworks, testNetworks] = useMemo(
+    () =>
+      Object.entries(networks).reduce(
+        ([nonTestnetsList, testnetsList], [chainId, network]) => {
+          try {
path: app/components/Views/confirmations/hooks/useEIP7702Networks.ts
user: login: jpuri
body: good point, let me remove try/catch block, again in following PR to avoid review loop
created_at: 2025-05-06T12:41:11Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-05-05T03:03:42Z
body: **CLA Signature Action:** All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-05T12:41:18Z
body: ## [<img alt="https://bitrise.io/" src="https://assets-global.website-files.com/5db35de024bb983af1b4e151/5e6f9ccc3e129dfd8a205e4e_Bitrise%20Logo%20-%20Eggplant%20Bg.png" height="20">](https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/208b25d1-1ad1-4e77-bd34-9bbeb12e07f2) **Bitrise**

✅✅✅ `pr_smoke_e2e_pipeline` passed on Bitrise! ✅✅✅

Commit hash: b7d5566e2dc0f99c5bf7abcb79fa19553840ef74
Build link: https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/208b25d1-1ad1-4e77-bd34-9bbeb12e07f2

>[!NOTE]
>- You can kick off another `pr_smoke_e2e_pipeline` on Bitrise by removing and re-applying the `Run Smoke E2E` label on the pull request

<!-- BITRISE_TAG -->
<!-- BITRISE_SUCCESS_TAG -->

<!-- b7d5566e2dc0f99c5bf7abcb79fa19553840ef74 -->
author_association: CONTRIBUTOR
user: login: sonarqubecloud[bot]
created_at: 2025-05-06T10:51:41Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=metamask-mobile&pullRequest=15104) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [5 New issues](https://sonarcloud.io/project/issues?id=metamask-mobile&pullRequest=15104&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=metamask-mobile&pullRequest=15104&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=metamask-mobile&pullRequest=15104&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [97.1% Coverage on New Code](https://sonarcloud.io/component_measures?id=metamask-mobile&pullRequest=15104&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=metamask-mobile&pullRequest=15104&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=metamask-mobile&pullRequest=15104)


author_association: NONE