Skip to content

Commit ac9b932

Browse files
jiexiffmcgee725
andauthored
feat: add connection id to simple deeplinks (#63)
* add session id to simple deeplinks * fix param * Update packages/connect-multichain/src/multichain/rpc/requestRouter.ts Co-authored-by: ffmcgee <51971598+ffmcgee725@users.noreply.github.com> * Update packages/connect-multichain/src/multichain/index.ts Co-authored-by: ffmcgee <51971598+ffmcgee725@users.noreply.github.com> * fix invoke test * add comment for getActiveSession * add log to getActiveSession try catch --------- Co-authored-by: ffmcgee <51971598+ffmcgee725@users.noreply.github.com>
1 parent 4ef28cc commit ac9b932

7 files changed

Lines changed: 65 additions & 26 deletions

File tree

packages/connect-multichain/src/domain/multichain/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { SessionRequest } from '@metamask/mobile-wallet-protocol-core';
1+
import type { Session, SessionRequest } from '@metamask/mobile-wallet-protocol-core';
22
import type {
33
Transport,
44
TransportRequest,
@@ -109,4 +109,6 @@ export type ExtendedTransport = Omit<Transport, 'connect'> & {
109109
timeout?: number;
110110
},
111111
) => Promise<TResponse>;
112+
113+
getActiveSession: () => Promise<Session | undefined>;
112114
};

packages/connect-multichain/src/invoke.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ function testSuite<T extends MultichainOptions>({ platform, createSDK, options:
124124
t.expect(sdk.state).toBe('connected');
125125
t.expect(sdk.storage).toBeDefined();
126126
t.expect(sdk.transport).toBeDefined();
127+
if (platform === 'web-mobile') {
128+
sdk.transport.getActiveSession = t.vi.fn().mockResolvedValue({id: 'mock-session-id'});
129+
}
127130

128131
const providerInvokeMethodSpy = t.vi.spyOn(RequestRouter.prototype, 'invokeMethod');
129132
const options = {
@@ -155,6 +158,10 @@ function testSuite<T extends MultichainOptions>({ platform, createSDK, options:
155158
await sdk.connect(scopes, caipAccountIds);
156159
t.expect(sdk.state).toBe('connected');
157160

161+
if (platform === 'web-mobile') {
162+
sdk.transport.getActiveSession = t.vi.fn().mockResolvedValue({id: 'mock-session-id'});
163+
}
164+
158165
const options = {
159166
scope: 'eip155:1',
160167
request: { method: 'eth_accounts', params: [] },
@@ -229,6 +236,10 @@ function testSuite<T extends MultichainOptions>({ platform, createSDK, options:
229236
t.expect(sdk.state).toBe('connected');
230237
t.expect(sdk.provider).toBeDefined();;
231238

239+
if (platform === 'web-mobile') {
240+
sdk.transport.getActiveSession = t.vi.fn().mockResolvedValue({id: 'mock-session-id'});
241+
}
242+
232243
await t.expect(sdk.invokeMethod(options)).rejects.toThrow('RPCErr53: RPC Client invoke method reason (Failed to invoke method)');
233244
});
234245
});

packages/connect-multichain/src/multichain/index.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -486,9 +486,9 @@ export class MultichainSDK extends MultichainCore {
486486
},
487487
};
488488
const deeplink =
489-
this.options.ui.factory.createDeeplink(connectionRequest);
489+
this.options.ui.factory.createConnectionDeeplink(connectionRequest);
490490
const universalLink =
491-
this.options.ui.factory.createUniversalLink(connectionRequest);
491+
this.options.ui.factory.createConnectionUniversalLink(connectionRequest);
492492
if (this.options.mobile?.preferredOpenLink) {
493493
this.options.mobile.preferredOpenLink(deeplink, '_self');
494494
} else {
@@ -715,13 +715,19 @@ export class MultichainSDK extends MultichainCore {
715715
const shouldOpenDeeplink = secure && !showInstallModal;
716716

717717
if (shouldOpenDeeplink) {
718-
setTimeout(() => {
718+
setTimeout(async () => {
719+
const session = await this.transport.getActiveSession();
720+
if (!session) {
721+
throw new Error('No active session found');
722+
}
723+
724+
const url = `${METAMASK_DEEPLINK_BASE}/mwp?id=${encodeURIComponent(session.id)}`;
719725
if (mobile?.preferredOpenLink) {
720-
mobile.preferredOpenLink(METAMASK_DEEPLINK_BASE, '_self');
726+
mobile.preferredOpenLink(url, '_self');
721727
} else {
722728
openDeeplink(
723729
this.options,
724-
METAMASK_DEEPLINK_BASE,
730+
url,
725731
METAMASK_CONNECT_BASE_URL,
726732
);
727733
}

packages/connect-multichain/src/multichain/rpc/requestRouter.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,17 @@ export class RequestRouter {
5353
const shouldOpenDeeplink = secure && !showInstallModal;
5454

5555
if (shouldOpenDeeplink) {
56-
setTimeout(() => {
56+
setTimeout(async () => {
57+
const session = await this.transport.getActiveSession();
58+
if (!session) {
59+
throw new Error('No active session found');
60+
}
61+
62+
const url = `${METAMASK_DEEPLINK_BASE}/mwp?id=${encodeURIComponent(session.id)}`;
5763
if (mobile?.preferredOpenLink) {
58-
mobile.preferredOpenLink(METAMASK_DEEPLINK_BASE, '_self');
64+
mobile.preferredOpenLink(url, '_self');
5965
} else {
60-
openDeeplink(this.config, METAMASK_DEEPLINK_BASE, METAMASK_CONNECT_BASE_URL);
66+
openDeeplink(this.config, url, METAMASK_CONNECT_BASE_URL);
6167
}
6268
}, 10); // small delay to ensure the message encryption and dispatch completes
6369
}
@@ -166,4 +172,3 @@ export class RequestRouter {
166172
return this.handleWithWallet(options);
167173
}
168174
}
169-

packages/connect-multichain/src/multichain/transports/default/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
getValidAccounts,
1515
isSameScopesAndAccounts,
1616
} from '../../utils';
17+
import { Session } from '@metamask/mobile-wallet-protocol-core';
1718

1819
const DEFAULT_REQUEST_TIMEOUT = 60 * 1000;
1920

@@ -309,4 +310,11 @@ export class DefaultTransport implements ExtendedTransport {
309310
this.#notificationCallbacks.delete(callback);
310311
};
311312
}
313+
314+
getActiveSession(): Promise<Session | undefined> {
315+
// This code path should never be triggered when the DefaultTransport is being used
316+
// It's only purpose is for exposing the session ID used for deeplinking to the mobile app
317+
// and so it is only implemented for the MWPTransport.
318+
throw new Error('getActiveSession is purposely not implemented for the DefaultTransport');
319+
}
312320
}

packages/connect-multichain/src/multichain/transports/mwp/index.ts

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -319,18 +319,11 @@ export class MWPTransport implements ExtendedTransport {
319319
scopes: Scope[];
320320
caipAccountIds: CaipAccountId[];
321321
}): Promise<void> {
322-
const { dappClient, kvstore } = this;
323-
const sessionStore = new SessionStore(kvstore);
322+
const { dappClient } = this;
324323

325-
let session: Session | undefined;
326-
try {
327-
const [activeSession] = await sessionStore.list();
328-
if (activeSession) {
329-
logger('active session found', activeSession);
330-
session = activeSession;
331-
}
332-
} catch {
333-
/* empty */
324+
const session = await this.getActiveSession();
325+
if (session) {
326+
logger('active session found', session);
334327
}
335328

336329
let timeout: NodeJS.Timeout;
@@ -592,4 +585,18 @@ export class MWPTransport implements ExtendedTransport {
592585
this.notificationCallbacks.delete(callback);
593586
};
594587
}
588+
589+
async getActiveSession(): Promise<Session | undefined> {
590+
const { kvstore } = this;
591+
const sessionStore = new SessionStore(kvstore);
592+
593+
try {
594+
const [activeSession] = await sessionStore.list();
595+
return activeSession;
596+
} catch (error){
597+
// TODO: verify if this try catch is necessary
598+
logger('error getting active session', error);
599+
return undefined;
600+
}
601+
}
595602
}

packages/connect-multichain/src/ui/index.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -145,17 +145,17 @@ export class ModalFactory<T extends FactoryModals = FactoryModals> {
145145
return container;
146146
}
147147

148-
createDeeplink(connectionRequest?: ConnectionRequest) {
148+
createConnectionDeeplink(connectionRequest?: ConnectionRequest) {
149149
if (!connectionRequest) {
150-
throw new Error('createDeeplink can only be called with a connection request');
150+
throw new Error('createConnectionDeeplink can only be called with a connection request');
151151
}
152152
const json = JSON.stringify(connectionRequest);
153153
const compressed = compressString(json);
154154
const urlEncoded = encodeURIComponent(compressed);
155155
return `${METAMASK_DEEPLINK_BASE}/mwp?p=${urlEncoded}&c=1`;
156156
}
157157

158-
createUniversalLink(connectionRequest?: ConnectionRequest) {
158+
createConnectionUniversalLink(connectionRequest?: ConnectionRequest) {
159159
if (!connectionRequest) {
160160
return `${METAMASK_CONNECT_BASE_URL}`;
161161
}
@@ -186,7 +186,7 @@ export class ModalFactory<T extends FactoryModals = FactoryModals> {
186186

187187
const parentElement = this.getMountedContainer();
188188
const connectionRequest = await createConnectionRequest();
189-
const qrCodeLink = this.createDeeplink(connectionRequest);
189+
const qrCodeLink = this.createConnectionDeeplink(connectionRequest);
190190

191191
const modal: Modal<any> = new this.options.InstallModal({
192192
expiresIn:
@@ -197,7 +197,7 @@ export class ModalFactory<T extends FactoryModals = FactoryModals> {
197197
link: qrCodeLink,
198198
sdkVersion: getVersion(),
199199
generateQRCode: async (request: ConnectionRequest) =>
200-
this.createDeeplink(request),
200+
this.createConnectionDeeplink(request),
201201
onClose: this.onCloseModal.bind(this),
202202
startDesktopOnboarding: this.onStartDesktopOnboarding.bind(this),
203203
createConnectionRequest,

0 commit comments

Comments
 (0)