Skip to content

Commit 4edef16

Browse files
authored
Add documentation for deep links (#158)
This PR adds contributor documentation for deep links. [Click here for rendered markdown version.](https://github.com/MetaMask/contributor-docs/blob/43d289e6c11cfcba2ec0819c06c217ca94c2c99c/docs/deep-links.md) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Introduces comprehensive contributor docs for deep links and surfaces them in the README. > > - Adds `docs/deep-links.md` covering overview, example behavior, user flow (mermaid), creating/signing links via `branch.io`, extension route registration (`pathname`, `getTitle`, `handler`), architecture class diagram, deferred deep links, and security notes > - Updates `README.md` Table of Contents to include `Deep links Guidelines` pointing to `docs/deep-links.md` > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 3b0900c. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 2a349ff commit 4edef16

File tree

2 files changed

+229
-0
lines changed

2 files changed

+229
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ This is a living repository — nothing is set in stone! If you're member of Met
2424
- [UI Integration Testing Guidelines](./docs/testing/ui-integration-testing.md)
2525
- [E2E Testing Guidelines](./docs/testing/e2e-testing.md)
2626
- [TypeScript Guidelines](./docs/typescript.md)
27+
- [Deep links Guidelines](./docs/deep-links.md)

docs/deep-links.md

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
# Deep links
2+
3+
## Overview
4+
5+
MetaMask deep links are smart URLs that dynamically route users to the most relevant point in their journey—based
6+
on their device, app status, and the action we want them to take.
7+
Deep links help reduce friction, personalize the journey, and drive higher conversion.
8+
For a link to be a deep link, it needs to have logic baked in.
9+
10+
### Example
11+
12+
Here is an example of a deep link which links to a swaps feature MetaMask extension.
13+
https://link.metamask.io/swap?sig=AWqgclBcX7wDKXJ-ZbABoRU2pzVS7xQAA5UsIuWEzKVchvqyYos_w0At4zR33_0wJdFAypIJM4VgboiU3ghhUQ
14+
15+
- Clicking on the deep link above would have the following behavior:
16+
- If a user is on DESKTOP:
17+
- And MetaMask extension is installed:
18+
- It will take the user to the Swap page.
19+
- And MetaMask extension is not installed:
20+
- It will take the user to the metamask.io/download website.
21+
22+
### Additional notes
23+
24+
- If a deep link is not registered in the extension, the error "Not found" page will be displayed.
25+
26+
### Deep link user flow
27+
28+
```mermaid
29+
---
30+
title: Example user flow diagram for deep linking to swaps page
31+
displayMode: compact
32+
---
33+
graph TD
34+
A[Click on a Deep Link for Swaps] -->|User on DESKTOP| B{MetaMask Extension Installed?}
35+
B -->|Yes| C{Is the Link Signed?}
36+
B -->|No| D[Take User to metamask.io/download]
37+
38+
C -->|Yes| E[Show interstitial page with link information]
39+
C -->|No| F[Show interstitial page with warning]
40+
41+
E --> |User Confirms| G[Take User to Swap Page]
42+
F -->|User Confirms| G
43+
```
44+
45+
## Creating and signing deep links
46+
47+
There are several steps that need to be taken to register a deep link in the MetaMask extension.
48+
49+
#### 1. Construct a deep link and sign it.
50+
51+
For deep link construction we use `branch.io` features.
52+
The Branch service is configured and automatically capable of linking to any path registered within the extension as a deep link route.
53+
So, adding `/home` or `/swap` at the end of `link.metamask.io` would be enough, and there is no need for additional configurations on the Branch side.
54+
55+
Signed deep links will skip the interstitial warning page and show the link information interstitial instead.
56+
For more information, please refer to the [ADR](https://github.com/MetaMask/decisions/blob/dcb42a5395507928e87c183dd1809c83d9cb408d/decisions/core/0007-deep-linking-into-wallet.md).
57+
58+
Links are signed with a protected dedicated link signer, so additional access is required (ask internally for link signer application and access).
59+
60+
Example:
61+
62+
```text
63+
https://link.metamask.io/home?sig=mmVgbZk8ucIY8-syEWrtnRvZlrHGQgM7Jl26fgRVjuwX62UVvhUE5nxOCWn0kEbYhZ_P17nwCZBbqJU7rPMx2w
64+
```
65+
66+
#### 2. Register deep link route in extension
67+
68+
Add a new file to the deep links route folder with a name of the route (e.g., home.ts) and create route definition.
69+
70+
```typescript
71+
import { DEFAULT_ROUTE, Route } from './route';
72+
73+
export default new Route({
74+
pathname: '/home',
75+
getTitle: (_: URLSearchParams) => 'deepLink_theHomePage', // deepLink_theHomePage is a translation constant from messages.json
76+
handler: function handler(_: URLSearchParams) {
77+
return { path: DEFAULT_ROUTE, query: new URLSearchParams() };
78+
},
79+
});
80+
```
81+
82+
Directory location: https://github.com/MetaMask/metamask-extension/tree/main/shared/lib/deep-links/routes
83+
84+
Add new route by calling `addRoute` utility function in index.ts file of the deep links route folder.
85+
86+
```typescript
87+
import home from './home';
88+
89+
addRoute(home);
90+
```
91+
92+
#### Additional notes
93+
94+
- Deep link route definition consists of:
95+
- `pathname` - Route path identifier (e.g., `/home`, `/swaps`, `/notifications`)
96+
- `getTitle` - Callback function that should return title of the deep link page. This is represented by the translation constant (e.g., `deepLink_theHomePage` from `messages.json`).
97+
- `handler` - Callback function that should return an object with `path` and `query` properties.
98+
- `path` - Exact path of the route used in extension. Taken from the existing routes definitions (e.g., `DEFAULT_ROUTE`, `NOTIFICATIONS_ROUTE`).
99+
- `query` - URL query params. Constructed using `URLSearchParams` constructor function.
100+
- Make sure that the route exists and return it from a handler under `path` key.
101+
- Handler function can transform query parameters if necessary.
102+
- Handler function must be synchronous.
103+
- Handler function must not change anything. Deep links are read-only, and all actions/changes must be confirmed by the user in the UI layer.
104+
- For a deeper understanding of the route definitions and their properties, [check router implementation and its types](https://github.com/MetaMask/metamask-extension/blob/main/shared/lib/deep-links/routes/route.ts#L18).
105+
- For a link to work as a deferred deep link, it needs to be added to the branch.io _LinkHub_. For more information check [deferred deep links section below](#deferred-deep-links).
106+
107+
## Architecture
108+
109+
For a deeper understanding of the deep links implementation and its architecture, here is the abstracted class diagram
110+
that shows associations and responsibilities of the particular components in the system.
111+
112+
```mermaid
113+
classDiagram
114+
class DeepLinkRouter {
115+
-getExtensionURL: Function
116+
-getState: Function
117+
+install()
118+
+uninstall()
119+
-handleBeforeRequest()
120+
-tryNavigateTo()
121+
-canSkipInterstitial()
122+
-formatUrlForInterstitialPage()
123+
-get404ErrorURL()
124+
-redirectTab()
125+
}
126+
127+
class DeepLink {
128+
-description: string
129+
-extraDescription: string
130+
-route: Route
131+
-title: string
132+
-cta: string
133+
-skipDeepLinkInterstitialChecked: boolean
134+
-isLoading: boolean
135+
+render()
136+
-updateStateFromUrl()
137+
-onRemindMeStateChanged()
138+
}
139+
140+
class Route {
141+
+pathname: string
142+
+getTitle: Function
143+
+handler: Function
144+
+constructor(options: RouteOptions)
145+
}
146+
147+
class ParsedDeepLink {
148+
+route: Route
149+
+destination: Destination
150+
+signature: SignatureStatus
151+
}
152+
153+
class Destination {
154+
+path: string
155+
+query: URLSearchParams
156+
OR
157+
+redirectTo: URL
158+
}
159+
160+
class EventEmitter {
161+
+emit()
162+
+on()
163+
}
164+
165+
class RouteRegistry {
166+
+routes: Map<string, Route>
167+
+addRoute(route: Route)
168+
}
169+
170+
%% Route implementations
171+
class HomeRoute {
172+
+pathname: "/home"
173+
+getTitle()
174+
+handler()
175+
}
176+
177+
class SwapRoute {
178+
+pathname: "/swap"
179+
+getTitle()
180+
+handler()
181+
}
182+
183+
class NotificationsRoute {
184+
+pathname: "/notifications"
185+
+getTitle()
186+
+handler()
187+
}
188+
189+
%% Define relationships
190+
EventEmitter <|-- DeepLinkRouter : extends
191+
DeepLinkRouter --> ParsedDeepLink : parses and creates
192+
DeepLinkRouter --> Route : uses routes from
193+
DeepLink --> ParsedDeepLink : displays and processes
194+
DeepLink --> Route : links to route
195+
ParsedDeepLink --> Route : contains
196+
ParsedDeepLink --> Destination : contains
197+
Route <|-- HomeRoute : implements
198+
Route <|-- SwapRoute : implements
199+
Route <|-- NotificationsRoute : implements
200+
RouteRegistry --> Route : registers and stores
201+
```
202+
203+
Deep Link Router is instantiated in the background script ([background.js](https://github.com/MetaMask/metamask-extension/blob/main/app/scripts/background.js#L757)).
204+
205+
### Deferred deep links
206+
207+
Deferred deep links are used to navigate a user to a deep link route after the extension is installed.
208+
For deferred deep link to work, it is required to add the specific link with its path to the branch.io _LinkHub_.
209+
210+
The extension receives deferred deep link data from the MetaMask website, which collects that data via the integrated Branch.io SDK.
211+
212+
Deferred deep link flow:
213+
214+
1. When a user lands on the MetaMask website download page, the website stores the deferred deep link inside cookie.
215+
2. When the user installs MetaMask extension, the extension reads and stores the cookie from the MetaMask website.
216+
3. Then extension navigates the user to the deferred deep link after successful onboarding.
217+
218+
Notes:
219+
220+
- Deferred deep link is stored in the extension's local storage immediately after installation.
221+
- User is navigated to the deferred deep link only if they completed the onboarding process within the two hours after installation.
222+
223+
## Security
224+
225+
It is recommended that deep links provided by MetaMask are signed.
226+
Signed links will skip the interstitial warning page and show the link information interstitial instead.
227+
Link information interstitial can be skipped next time if a user checks the "Don't remind me again" option.
228+
By signing links, it is ensured that there is a distinction between what MetaMask provides and what is coming from a community outside.

0 commit comments

Comments
 (0)