Repository: MetaMask/metamask-mobile
Pull request #14364: feat: bridge solana bridge button
Author: login: infiniteflower
Labels: name: team-swaps-and-bridge
description: Swaps and Bridge team
name: release-7.45.0
description: Issue or pull request that will be included in release 7.45.0
Created: 2025-04-02T01:50:04Z | Merged: 2025-04-09T15:36:39Z

<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once.
-->

## **Description**

<!--
Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->

This PR shows the Swap and Bridge buttons when you are on the Solana network. It's hidden behind the codefence `keyring-snaps`

## **Related issues**

Fixes:

## **Manual testing steps**

1. Add the following  to `.js.env`

```
export MM_BRIDGE_UI_ENABLED="true"
export PORTFOLIO_VIEW="true"
export METAMASK_BUILD_TYPE="flask"
```

2. Make a Solana account
3. Click on wallet actions
4. Observe Bridge button and click on it to go to Bridge

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

<img width="544" alt="Screenshot 2025-04-01 at 9 51 51 PM" src="https://github.com/user-attachments/assets/e6fddf22-0d8c-443c-86dd-02ee1ecf36c1" />


## **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
- [ ] I’ve included tests if applicable
- [ ] 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/components/UI/Bridge/utils/index.ts b/app/components/UI/Bridge/utils/index.ts
new file mode 100644
index 00000000000..5af837c146e
--- /dev/null
+++ b/app/components/UI/Bridge/utils/index.ts
@@ -0,0 +1 @@
+export const isBridgeUiEnabled = () => process.env.MM_BRIDGE_UI_ENABLED === 'true';
diff --git a/app/components/UI/Bridge/utils/isBridgeAllowed.test.ts b/app/components/UI/Bridge/utils/isBridgeAllowed.test.ts
new file mode 100644
index 00000000000..2c246e33dcf
--- /dev/null
+++ b/app/components/UI/Bridge/utils/isBridgeAllowed.test.ts
@@ -0,0 +1,81 @@
+import isBridgeAllowed from './isBridgeAllowed';
+import AppConstants from '../../../../core/AppConstants';
+import { NETWORKS_CHAIN_ID } from '../../../../constants/network';
+import { BtcScope, SolScope } from '@metamask/keyring-api';
+import { isBridgeUiEnabled } from './';
+// Mock AppConstants
+jest.mock('../../../../core/AppConstants', () => ({
+  BRIDGE: {
+    ACTIVE: true,
+  },
+}));
+
+jest.mock('.', () => ({
+  __esModule: true,
+  isBridgeUiEnabled: jest.fn(() => true),
+}));
+
+describe('isBridgeAllowed', () => {
+  const {
+    MAINNET,
+    OPTIMISM,
+    BSC,
+    POLYGON,
+    ZKSYNC_ERA: ZKSYNC,
+    BASE,
+    ARBITRUM,
+    AVAXCCHAIN: AVALANCHE,
+    LINEA_MAINNET: LINEA,
+  } = NETWORKS_CHAIN_ID;
+
+  describe('when BRIDGE.ACTIVE is false', () => {
+    beforeEach(() => {
+      (AppConstants.BRIDGE.ACTIVE as boolean) = false;
+    });
+
+    it('should return false for any chain ID', () => {
+      expect(isBridgeAllowed(MAINNET)).toBe(false);
+      expect(isBridgeAllowed(OPTIMISM)).toBe(false);
+      expect(isBridgeAllowed('0x999')).toBe(false);
+    });
+  });
+
+  describe('when BRIDGE.ACTIVE is true', () => {
+    beforeEach(() => {
+      (AppConstants.BRIDGE.ACTIVE as boolean) = true;
+    });
+
+    it('should return true for allowed chain IDs', () => {
+      expect(isBridgeAllowed(MAINNET)).toBe(true);
+      expect(isBridgeAllowed(OPTIMISM)).toBe(true);
+      expect(isBridgeAllowed(BSC)).toBe(true);
+      expect(isBridgeAllowed(POLYGON)).toBe(true);
+      expect(isBridgeAllowed(ZKSYNC)).toBe(true);
+      expect(isBridgeAllowed(BASE)).toBe(true);
+      expect(isBridgeAllowed(ARBITRUM)).toBe(true);
+      expect(isBridgeAllowed(AVALANCHE)).toBe(true);
+      expect(isBridgeAllowed(LINEA)).toBe(true);
+    });
+
+    it('should return false for non-allowed chain IDs', () => {
+      expect(isBridgeAllowed('0x999')).toBe(false);
+      expect(isBridgeAllowed('0x123')).toBe(false);
+    });
+
+    it('should return false for Bitcoin mainnet', () => {
+      expect(isBridgeAllowed(BtcScope.Mainnet)).toBe(false);
+    });
+
+    describe('Solana mainnet handling', () => {
+      it('should return true for Solana mainnet when bridge UI is enabled', () => {
+        (isBridgeUiEnabled as jest.Mock).mockReturnValue(true);
+        expect(isBridgeAllowed(SolScope.Mainnet)).toBe(true);
+      });
+
+      it('should return false for Solana mainnet when bridge UI is disabled', () => {
+        (isBridgeUiEnabled as jest.Mock).mockReturnValue(false);
+        expect(isBridgeAllowed(SolScope.Mainnet)).toBe(false);
+      });
+    });
+  });
+});
diff --git a/app/components/UI/Bridge/utils/isBridgeAllowed.ts b/app/components/UI/Bridge/utils/isBridgeAllowed.ts
index 9f54bfc7ede..13e55aff8c4 100644
--- a/app/components/UI/Bridge/utils/isBridgeAllowed.ts
+++ b/app/components/UI/Bridge/utils/isBridgeAllowed.ts
@@ -1,7 +1,16 @@
 import AppConstants from '../../../../core/AppConstants';
 import { NETWORKS_CHAIN_ID } from '../../../../constants/network';
 import { CaipChainId, Hex } from '@metamask/utils';
-import { isNonEvmChainId } from '../../../../core/Multichain/utils';
+import {
+  BtcScope,
+  ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
+  SolScope,
+  ///: END:ONLY_INCLUDE_IF(keyring-snaps)
+} from '@metamask/keyring-api';
+
+///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
+import { isBridgeUiEnabled } from './';
+///: END:ONLY_INCLUDE_IF(keyring-snaps)
 
 const {
   MAINNET,
@@ -35,7 +44,13 @@ const allowedChainIds = [
 export default function isBridgeAllowed(chainId: Hex | CaipChainId) {
   if (!AppConstants.BRIDGE.ACTIVE) return false;
 
-  if (isNonEvmChainId(chainId)) {
+  ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
+  if (chainId === SolScope.Mainnet && isBridgeUiEnabled()) {
+    return true;
+  }
+  ///: END:ONLY_INCLUDE_IF(keyring-snaps)
+
+  if (chainId === BtcScope.Mainnet) {
     return false;
   }
 
diff --git a/app/components/UI/Swaps/utils/index.js b/app/components/UI/Swaps/utils/index.js
index 9f560f67d14..acee50ca886 100644
--- a/app/components/UI/Swaps/utils/index.js
+++ b/app/components/UI/Swaps/utils/index.js
@@ -4,6 +4,10 @@ import { swapsUtils } from '@metamask/swaps-controller';
 import { strings } from '../../../../../locales/i18n';
 import AppConstants from '../../../../core/AppConstants';
 import { NETWORKS_CHAIN_ID } from '../../../../constants/network';
+///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
+import { SolScope } from '@metamask/keyring-api';
+///: END:ONLY_INCLUDE_IF(keyring-snaps)
+import { isBridgeUiEnabled } from '../../Bridge/utils';
 
 const {
   ETH_CHAIN_ID,
@@ -47,6 +51,13 @@ export function isSwapsAllowed(chainId) {
   if (!AppConstants.SWAPS.ONLY_MAINNET) {
     allowedChainIds.push(SWAPS_TESTNET_CHAIN_ID);
   }
+
+  ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
+  if (chainId === SolScope.Mainnet && isBridgeUiEnabled()) {
+    return true;
+  }
+  ///: END:ONLY_INCLUDE_IF(keyring-snaps)
+
   return allowedChainIds.includes(chainId);
 }
 
diff --git a/app/components/UI/Swaps/utils/index.test.js b/app/components/UI/Swaps/utils/index.test.js
index 511e58e1010..998e58ad804 100644
--- a/app/components/UI/Swaps/utils/index.test.js
+++ b/app/components/UI/Swaps/utils/index.test.js
@@ -1,4 +1,34 @@
-import { getFetchParams, shouldShowMaxBalanceLink } from './index';
+import {
+  getFetchParams,
+  shouldShowMaxBalanceLink,
+  isSwapsAllowed,
+} from './index';
+import { swapsUtils } from '@metamask/swaps-controller';
+import { SolScope } from '@metamask/keyring-api';
+
+// Mock AppConstants
+const mockSwapsConstantsGetter = jest.fn(() => ({
+  ACTIVE: true,
+  ONLY_MAINNET: true,
+}));
+jest.mock('../../../../core/AppConstants', () => ({
+  get SWAPS() {
+    return mockSwapsConstantsGetter();
+  },
+}));
+
+const {
+  ETH_CHAIN_ID,
+  BSC_CHAIN_ID,
+  SWAPS_TESTNET_CHAIN_ID,
+  POLYGON_CHAIN_ID,
+  AVALANCHE_CHAIN_ID,
+  ARBITRUM_CHAIN_ID,
+  OPTIMISM_CHAIN_ID,
+  ZKSYNC_ERA_CHAIN_ID,
+  LINEA_CHAIN_ID,
+  BASE_CHAIN_ID,
+} = swapsUtils;
 
 describe('getFetchParams', () => {
   const mockSourceToken = {
@@ -157,3 +187,66 @@ describe('shouldShowMaxBalanceLink', () => {
     expect(result).toBe(false);
   });
 });
+
+describe('isSwapsAllowed', () => {
+  beforeEach(() => {
+    mockSwapsConstantsGetter.mockReturnValue({
+      ACTIVE: true,
+      ONLY_MAINNET: true,
+    });
+  });
+
+  it('should return false when swaps are not active', () => {
+    // Mock AppConstants.SWAPS.ACTIVE to false
+    mockSwapsConstantsGetter.mockReturnValue({
+      ...mockSwapsConstantsGetter(),
+      ACTIVE: false,
+    });
+
+    expect(isSwapsAllowed(ETH_CHAIN_ID)).toBe(false);
+  });
+
+  test.each([
+    [ETH_CHAIN_ID],
+    [BSC_CHAIN_ID],
+    [POLYGON_CHAIN_ID],
+    [AVALANCHE_CHAIN_ID],
+    [ARBITRUM_CHAIN_ID],
+    [OPTIMISM_CHAIN_ID],
+    [ZKSYNC_ERA_CHAIN_ID],
+    [LINEA_CHAIN_ID],
+    [BASE_CHAIN_ID],
+  ])('should return true for chain ID %s', (chainId) => {
+    expect(isSwapsAllowed(chainId)).toBe(true);
+  });
+
+  it('should return false for Solana mainnet when bridge UI is disabled', () => {
+    process.env.MM_BRIDGE_UI_ENABLED = 'false';
+    expect(isSwapsAllowed(SolScope.Mainnet)).toBe(false);
+  });
+
+  it('should return false for unsupported chain IDs', () => {
+    const unsupportedChainId = '0x9999';
+    expect(isSwapsAllowed(unsupportedChainId)).toBe(false);
+  });
+
+  describe('testnet chain IDs', () => {
+    it('should return true for testnet chain IDs in development when ONLY_MAINNET is true', () => {
+      global.__DEV__ = true;
+      mockSwapsConstantsGetter.mockReturnValue({
+        ...mockSwapsConstantsGetter(),
+        ONLY_MAINNET: true,
+      });
+      expect(isSwapsAllowed(SWAPS_TESTNET_CHAIN_ID)).toBe(true);
+    });
+
+    it('should return true for testnet chain IDs when ONLY_MAINNET is false', () => {
+      global.__DEV__ = false;
+      mockSwapsConstantsGetter.mockReturnValue({
+        ...mockSwapsConstantsGetter(),
+        ONLY_MAINNET: false,
+      });
+      expect(isSwapsAllowed(SWAPS_TESTNET_CHAIN_ID)).toBe(true);
+    });
+  });
+});
diff --git a/app/components/Views/WalletActions/WalletActions.test.tsx b/app/components/Views/WalletActions/WalletActions.test.tsx
index 824f6ab22e4..baf0393c089 100644
--- a/app/components/Views/WalletActions/WalletActions.test.tsx
+++ b/app/components/Views/WalletActions/WalletActions.test.tsx
@@ -7,6 +7,7 @@ import {
 } from '../../../selectors/accountsController';
 import { isSwapsAllowed } from '../../../components/UI/Swaps/utils';
 import isBridgeAllowed from '../../UI/Bridge/utils/isBridgeAllowed';
+import { SolScope , EthAccountType, SolAccountType } from '@metamask/keyring-api';
 
 import renderWithProvider, {
   DeepPartial,
@@ -25,7 +26,6 @@ import {
 import Engine from '../../../core/Engine';
 import { isStablecoinLendingFeatureEnabled } from '../../UI/Stake/constants';
 import { sendMultichainTransaction } from '../../../core/SnapKeyring/utils/sendMultichainTransaction';
-import { EthAccountType, SolAccountType } from '@metamask/keyring-api';
 
 jest.mock('../../../core/SnapKeyring/utils/sendMultichainTransaction', () => ({
   sendMultichainTransaction: jest.fn(),
@@ -323,6 +323,47 @@ describe('WalletActions', () => {
     expect(mockNavigate).toHaveBeenCalled();
   });
 
+  it('should call the goToBridge function when the Swap button is pressed on Solana mainnet', () => {
+    (isSwapsAllowed as jest.Mock).mockReturnValue(true);
+    (selectChainId as unknown as jest.Mock).mockReturnValue(SolScope.Mainnet);
+
+    const { getByTestId } = renderWithProvider(<WalletActions />, {
+      state: mockInitialState,
+    });
+
+    fireEvent.press(
+      getByTestId(WalletActionsBottomSheetSelectorsIDs.SWAP_BUTTON),
+    );
+
+    expect(mockNavigate).toHaveBeenCalledWith('BrowserTabHome', {
+      params: {
+        newTabUrl: 'https://bridge.metamask.io/?metamaskEntry=mobile&srcChain=1',
+        timestamp: 123,
+      },
+      screen: 'BrowserView',
+    });
+  });
+
+  it('should call the goToSwaps function when the Swap button is pressed not on Solana testnet', () => {
+    (isSwapsAllowed as jest.Mock).mockReturnValue(true);
+    (selectChainId as unknown as jest.Mock).mockReturnValue('0x1');
+
+    const { getByTestId } = renderWithProvider(<WalletActions />, {
+      state: mockInitialState,
+    });
+
+    fireEvent.press(
+      getByTestId(WalletActionsBottomSheetSelectorsIDs.SWAP_BUTTON),
+    );
+
+    expect(mockNavigate).toHaveBeenCalledWith('Swaps', {
+      params: {
+        sourcePage: 'MainView',
+        sourceToken: '0x0000000000000000000000000000000000000000',
+      },
+      screen: 'SwapsAmountView',
+    });
+  });
   it('should call the goToBridge function when the Bridge button is pressed', () => {
     (isBridgeAllowed as jest.Mock).mockReturnValue(true);
     const { getByTestId } = renderWithProvider(<WalletActions />, {
diff --git a/app/components/Views/WalletActions/WalletActions.tsx b/app/components/Views/WalletActions/WalletActions.tsx
index f1f6b8722d2..8507b853a6a 100644
--- a/app/components/Views/WalletActions/WalletActions.tsx
+++ b/app/components/Views/WalletActions/WalletActions.tsx
@@ -45,7 +45,7 @@ import { isStablecoinLendingFeatureEnabled } from '../../UI/Stake/constants';
 import { EVENT_LOCATIONS as STAKE_EVENT_LOCATIONS } from '../../UI/Stake/constants/events';
 ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
 import { CaipChainId, SnapId } from '@metamask/snaps-sdk';
-import { isEvmAccountType } from '@metamask/keyring-api';
+import { isEvmAccountType, SolScope } from '@metamask/keyring-api';
 import { isMultichainWalletSnap } from '../../../core/SnapKeyring/utils/snaps';
 // eslint-disable-next-line no-duplicate-imports, import/no-duplicates
 import { selectSelectedInternalAccount } from '../../../selectors/accountsController';
@@ -304,6 +304,23 @@ const WalletActions = () => {
     [styles.icon],
   );
 
+  const onPressSwaps = useCallback(() => {
+    ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
+    if (chainId === SolScope.Mainnet) {
+      goToBridge();
+      return;
+    }
+    ///: END:ONLY_INCLUDE_IF
+
+    goToSwaps();
+  }, [
+    ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
+    chainId,
+    goToBridge,
+    ///: END:ONLY_INCLUDE_IF
+    goToSwaps,
+  ]);
+
   return (
     <BottomSheet ref={sheetRef}>
       <View style={styles.actionsContainer}>
@@ -332,7 +349,7 @@ const WalletActions = () => {
           <WalletAction
             actionType={WalletActionType.Swap}
             iconName={IconName.SwapHorizontal}
-            onPress={goToSwaps}
+            onPress={onPressSwaps}
             actionID={WalletActionsBottomSheetSelectorsIDs.SWAP_BUTTON}
             iconStyle={styles.icon}
             iconSize={AvatarSize.Md}


Reviews:
user: login: wachunei
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-08T15:41:42Z
user: login: wachunei
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-08T15:42:33Z
user: login: wachunei
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-08T15:44:27Z
user: login: infiniteflower
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T15:46:26Z
user: login: infiniteflower
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T15:46:50Z
user: login: micaelae
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-04-08T16:18:18Z
user: login: micaelae
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-04-08T18:28:42Z
user: login: infiniteflower
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T18:40:50Z
user: login: ghgoodreau
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T18:46:22Z
user: login: wachunei
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-04-08T19:22:12Z
user: login: wachunei
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-09T14:41:26Z
user: login: ghgoodreau
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T14:47:20Z
user: login: micaelae
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-09T14:48:45Z

Review comments:
diff_hunk: @@ -0,0 +1 @@
+export const isBridgeUiEnabled = () => process.env.MM_BRIDGE_UI_ENABLED === 'true';
path: app/components/UI/Bridge/utils/index.ts
user: login: wachunei
body: is this needed to be a function? It can be a constant, for example: 
https://github.com/MetaMask/metamask-mobile/blob/07516828b217a76280b9d911c2f3d94416460528/app/components/UI/Ramp/sdk/index.tsx#L43
created_at: 2025-04-08T15:41:42Z
author_association: MEMBER
diff_hunk: @@ -47,6 +48,16 @@ export function isSwapsAllowed(chainId) {
   if (!AppConstants.SWAPS.ONLY_MAINNET) {
     allowedChainIds.push(SWAPS_TESTNET_CHAIN_ID);
   }
+
+  ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
+  if (
+    chainId === SolScope.Mainnet &&
+    process.env.MM_BRIDGE_UI_ENABLED === 'true'
path: app/components/UI/Swaps/utils/index.js
user: login: wachunei
body:  the `isBridgeUiEnabled` constant should be used here
created_at: 2025-04-08T15:42:32Z
author_association: MEMBER
diff_hunk: @@ -332,7 +332,7 @@ const WalletActions = () => {
           <WalletAction
             actionType={WalletActionType.Swap}
             iconName={IconName.SwapHorizontal}
-            onPress={goToSwaps}
+            onPress={chainId === SolScope.Mainnet ? goToBridge : goToSwaps}
path: app/components/Views/WalletActions/WalletActions.tsx
user: login: wachunei
body:  just a quick question: in the near future Swap and Bridge will unify and point to the same new Bridge UI correct?
created_at: 2025-04-08T15:44:27Z
author_association: MEMBER
diff_hunk: @@ -332,7 +332,7 @@ const WalletActions = () => {
           <WalletAction
             actionType={WalletActionType.Swap}
             iconName={IconName.SwapHorizontal}
-            onPress={goToSwaps}
+            onPress={chainId === SolScope.Mainnet ? goToBridge : goToSwaps}
path: app/components/Views/WalletActions/WalletActions.tsx
user: login: infiniteflower
body: Yes that is correct. For now, only Solana, Swap and Bridge buttons both take a user to the Bridge UI.
created_at: 2025-04-08T15:46:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1 @@
+export const isBridgeUiEnabled = () => process.env.MM_BRIDGE_UI_ENABLED === 'true';
path: app/components/UI/Bridge/utils/index.ts
user: login: infiniteflower
body: Made it a function for ease of mocking in tests
created_at: 2025-04-08T15:46:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -47,6 +48,16 @@ export function isSwapsAllowed(chainId) {
   if (!AppConstants.SWAPS.ONLY_MAINNET) {
     allowedChainIds.push(SWAPS_TESTNET_CHAIN_ID);
   }
+
+  ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
+  if (
+    chainId === SolScope.Mainnet &&
+    process.env.MM_BRIDGE_UI_ENABLED === 'true'
path: app/components/UI/Swaps/utils/index.js
user: login: infiniteflower
body: Fixed in [35e0500](https://github.com/MetaMask/metamask-mobile/pull/14364/commits/35e050034bc0b0ad95271e27dc586e8d113e44df)
created_at: 2025-04-08T18:40:50Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-04-02T01:50:16Z
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-04-02T01:51: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/b8c89830-51ff-425c-84f3-3b994c2083c3) **Bitrise**

❌❌❌ `pr_smoke_e2e_pipeline` failed on Bitrise! ❌❌❌

Commit hash: fd6e0c4cb17495ee0836b46aaaa29954efc7686f
Build link: https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/b8c89830-51ff-425c-84f3-3b994c2083c3

>[!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

> [!TIP]
>- Check the [documentation](https://www.notion.so/metamask-consensys/Bitrise-Pipeline-Overview-43159500c43748a389556f0593e8834b#26052f2ea6e24f8c9cfdb57a7522dc1f) if you have any doubts on how to understand the failure on bitrise

<!-- BITRISE_TAG -->
<!-- BITRISE_FAIL_TAG -->

<!-- fd6e0c4cb17495ee0836b46aaaa29954efc7686f -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-08T18:22:46Z
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/9f946d71-a09a-48e5-b418-080ea92fcecf) **Bitrise**

❌❌❌ `pr_smoke_e2e_pipeline` failed on Bitrise! ❌❌❌

Commit hash: bf076f2111c8ae97b6b58732c29f6f8e779dc23e
Build link: https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/9f946d71-a09a-48e5-b418-080ea92fcecf

>[!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

> [!TIP]
>- Check the [documentation](https://www.notion.so/metamask-consensys/Bitrise-Pipeline-Overview-43159500c43748a389556f0593e8834b#26052f2ea6e24f8c9cfdb57a7522dc1f) if you have any doubts on how to understand the failure on bitrise

<!-- BITRISE_TAG -->
<!-- BITRISE_FAIL_TAG -->

<!-- bf076f2111c8ae97b6b58732c29f6f8e779dc23e -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-08T21:56:49Z
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/5fc57448-61ed-49f0-888d-a7ddd7369859) **Bitrise**

❌❌❌ `pr_smoke_e2e_pipeline` failed on Bitrise! ❌❌❌

Commit hash: ae6f15091d2ebcbdc98e517faca60a3bbfe6cbf4
Build link: https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/5fc57448-61ed-49f0-888d-a7ddd7369859

>[!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

> [!TIP]
>- Check the [documentation](https://www.notion.so/metamask-consensys/Bitrise-Pipeline-Overview-43159500c43748a389556f0593e8834b#26052f2ea6e24f8c9cfdb57a7522dc1f) if you have any doubts on how to understand the failure on bitrise

<!-- BITRISE_TAG -->
<!-- BITRISE_FAIL_TAG -->

<!-- ae6f15091d2ebcbdc98e517faca60a3bbfe6cbf4 -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-08T22:28:53Z
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/abab30fb-ec98-4471-804e-6f688ee2bd66) **Bitrise**

❌❌❌ `pr_smoke_e2e_pipeline` failed on Bitrise! ❌❌❌

Commit hash: 67cdea28a73fff7d31f0be4336ef21e3c890fd5a
Build link: https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/abab30fb-ec98-4471-804e-6f688ee2bd66

>[!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

> [!TIP]
>- Check the [documentation](https://www.notion.so/metamask-consensys/Bitrise-Pipeline-Overview-43159500c43748a389556f0593e8834b#26052f2ea6e24f8c9cfdb57a7522dc1f) if you have any doubts on how to understand the failure on bitrise

<!-- BITRISE_TAG -->
<!-- BITRISE_FAIL_TAG -->

<!-- 67cdea28a73fff7d31f0be4336ef21e3c890fd5a -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-09T13:59:10Z
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/cdbd28da-54af-4d8d-8fbd-1dcb6067d6f5) **Bitrise**

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

Commit hash: 92721af912b792eb614c0f614f916603b0f34222
Build link: https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/cdbd28da-54af-4d8d-8fbd-1dcb6067d6f5

>[!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 -->

<!-- 92721af912b792eb614c0f614f916603b0f34222 -->
author_association: CONTRIBUTOR
user: login: sonarqubecloud[bot]
created_at: 2025-04-09T14:19:36Z
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=14364) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=metamask-mobile&pullRequest=14364&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=14364&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=14364&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [91.3% Coverage on New Code](https://sonarcloud.io/component_measures?id=metamask-mobile&pullRequest=14364&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=14364&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=metamask-mobile&pullRequest=14364)


author_association: NONE