Skip to content

Conversation

@smilingkylan
Copy link
Contributor

@smilingkylan smilingkylan commented Oct 31, 2025

Description

This PR fixes an issue where MetaMask deeplinks clicked within the in-app browser or carousel banners were redirecting users to the app store instead of navigating internally within the app.

Problem

When users clicked MetaMask deeplinks (e.g., https://link.metamask.io/swap, metamask://dapp/uniswap.org) from within the app:

  • In-App Browser: Links would attempt to open the app externally, causing an app store redirect
  • Carousel Banners: Action links would trigger OS-level handling instead of internal navigation

This occurred because these URLs were being passed to the OS linking system (Linking.openURL()), which would try to open MetaMask again, resulting in an app store redirect since the app was already running.

Solution

The fix intercepts MetaMask deeplinks and routes them through SharedDeeplinkManager for internal handling:

  1. Created utility function (isInternalDeepLink) to identify MetaMask URLs that should be handled internally
  2. Updated BrowserTab to intercept deeplinks in onShouldStartLoadWithRequest and use browserCallBack for in-browser navigation
  3. Updated Carousel to differentiate between internal deeplinks and external URLs
  4. Added comprehensive unit tests covering all deeplink types and edge cases

The browserCallBack mechanism allows deeplinks that should stay in the browser (e.g., dapp:// URLs) to navigate the current WebView tab, while other deeplinks (e.g., swap, buy-crypto) navigate to their respective app screens.

Changelog

CHANGELOG entry: Fixed MetaMask deeplinks clicked within the in-app browser or carousel banners redirecting to the app store instead of navigating internally

Related issues

Fixes: #[ISSUE_NUMBER]

Manual testing steps

Feature: Internal deeplink handling in browser

  Scenario: user clicks MetaMask dapp deeplink in browser
    Given user has opened a website in the in-app browser
    And the website contains a link to "https://link.metamask.io/dapp/uniswap.org"
    
    When user taps the deeplink
    Then the current browser tab navigates to "https://uniswap.org"
    And the user remains in the browser (no app store redirect)

  Scenario: user clicks MetaMask feature deeplink in browser
    Given user has opened a website in the in-app browser
    And the website contains a link to "https://link.metamask.io/swap"
    
    When user taps the deeplink
    Then the app navigates to the Swap screen
    And the browser is exited

  Scenario: user clicks MetaMask deeplink in carousel
    Given user is viewing the wallet home screen
    And a carousel banner contains a MetaMask deeplink
    
    When user taps the carousel banner
    Then the app navigates to the appropriate feature screen
    And no app store redirect occurs

  Scenario: user clicks external link in browser
    Given user has opened a website in the in-app browser
    And the website contains a regular external link
    
    When user taps the external link
    Then the link behaves as normal (opens in browser or externally)

Feature: Deeplink type detection

  Scenario: utility correctly identifies internal deeplinks
    Given various URL types are tested
    
    When "https://link.metamask.io/swap" is checked
    Then it is identified as internal deeplink
    
    When "metamask://dapp/uniswap.org" is checked
    Then it is identified as internal deeplink
    
    When "https://google.com" is checked
    Then it is identified as external URL

Additional Testing Steps:

  1. Browser Test Page: Create an HTML page with test links covering:

    • Universal links: https://link.metamask.io/dapp/uniswap.org
    • Custom schemes: metamask://dapp/app.aave.com, dapp://curve.fi
    • Feature links: https://link.metamask.io/swap, /buy-crypto, /home
    • External links: https://ethereum.org (control group)
  2. Unit Tests: Run yarn jest app/util/deeplinks/index.test.ts

  3. TypeScript: Run yarn lint:tsc to verify type safety

Screenshots/Recordings

Before

After

Pre-merge author checklist

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.

Note

Route internal MetaMask deeplinks to in-app handling in Browser and Carousel, opening only external URLs via OS, with new util and tests.

  • Deeplink handling
    • Add isInternalDeepLink util to detect MetaMask deeplinks; expose shouldOpenExternally.
    • Use SharedDeeplinkManager.parse for internal links and Linking.openURL for external ones.
  • Components
    • app/components/Views/BrowserTab/BrowserTab.tsx:
      • Intercept internal deeplinks in onShouldStartLoadWithRequest; handle with SharedDeeplinkManager.parse using { origin: 'in-app-browser', browserCallBack }; prevent WebView load.
    • app/components/UI/Carousel/index.tsx:
      • Differentiate internal vs external URLs in openUrl; use { origin: 'carousel' } when parsing internal.
  • Constants
    • app/core/AppConstants.ts: add DEEPLINKS.ORIGIN_CAROUSEL.
  • Tests
    • app/util/deeplinks/index.test.ts: add comprehensive tests for isInternalDeepLink.
    • app/components/UI/Carousel/index.test.tsx: mock Linking, assert external opens via Linking and internal via SharedDeeplinkManager with AppConstants.DEEPLINKS.ORIGIN_CAROUSEL.

Written by Cursor Bugbot for commit 191fdce. This will update automatically on new commits. Configure here.

@github-actions
Copy link
Contributor

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.

@metamaskbot metamaskbot added the team-mobile-platform Mobile Platform team label Oct 31, 2025
@smilingkylan smilingkylan changed the title Allow deeplinking from within in-app browser feat: Allow deeplinking from within in-app browser Oct 31, 2025
@smilingkylan smilingkylan marked this pull request as ready for review October 31, 2025 19:17
@smilingkylan smilingkylan requested a review from a team as a code owner October 31, 2025 19:17
@smilingkylan smilingkylan requested a review from a team as a code owner October 31, 2025 19:19
cursor[bot]

This comment was marked as outdated.

cursor[bot]

This comment was marked as outdated.

@sonarqubecloud
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size-M team-mobile-platform Mobile Platform team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants