Skip to content

Commit 3859650

Browse files
committed
handle tracking for auto-connection in the background for MM and WC
1 parent 0ef37e6 commit 3859650

File tree

4 files changed

+112
-42
lines changed

4 files changed

+112
-42
lines changed

packages/no-modal/src/base/connector/interfaces.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
type WEB3AUTH_NETWORK_TYPE,
1515
} from "@web3auth/auth";
1616

17+
import { type Analytics } from "../analytics";
1718
import type { ChainNamespaceType, ConnectorNamespaceType, CustomChainConfig } from "../chain/IChainInterface";
1819
import type { IWeb3AuthCoreOptions } from "../core/IWeb3Auth";
1920
import { Web3AuthError } from "../errors";
@@ -45,6 +46,7 @@ export type IdentityTokenInfo = { idToken: string };
4546

4647
export interface BaseConnectorSettings {
4748
coreOptions: IWeb3AuthCoreOptions;
49+
analytics?: Analytics;
4850
}
4951

5052
export interface IProvider extends SafeEventEmitter<ProviderEvents> {
@@ -88,6 +90,7 @@ export interface IConnector<T> extends SafeEventEmitter {
8890
export type ConnectorParams = {
8991
projectConfig?: ProjectConfig;
9092
coreOptions: IWeb3AuthCoreOptions;
93+
analytics: Analytics;
9194
};
9295

9396
export type BaseConnectorLoginParams = {

packages/no-modal/src/connectors/metamask-connector/metamaskConnector.ts

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import { MetaMaskSDK, type MetaMaskSDKOptions } from "@metamask/sdk";
2+
import { getErrorAnalyticsProperties } from "@toruslabs/base-controllers";
23
import deepmerge from "deepmerge";
34

45
import {
6+
type Analytics,
7+
ANALYTICS_EVENTS,
58
BaseConnectorLoginParams,
69
type BaseConnectorSettings,
710
CHAIN_NAMESPACES,
@@ -18,6 +21,7 @@ import {
1821
type ConnectorNamespaceType,
1922
type ConnectorParams,
2023
type CustomChainConfig,
24+
getCaipChainId,
2125
type IProvider,
2226
type MetaMaskConnectorData,
2327
type UserInfo,
@@ -50,9 +54,12 @@ class MetaMaskConnector extends BaseEvmConnector<void> {
5054

5155
private metamaskOptions: Partial<MetaMaskSDKOptions>;
5256

57+
private analytics?: Analytics;
58+
5359
constructor(connectorOptions: MetaMaskConnectorOptions) {
5460
super(connectorOptions);
5561
this.metamaskOptions = connectorOptions.connectorSettings;
62+
this.analytics = connectorOptions.analytics;
5663
}
5764

5865
get provider(): IProvider | null {
@@ -112,6 +119,21 @@ class MetaMaskConnector extends BaseEvmConnector<void> {
112119
if (!this.metamaskSDK) throw WalletLoginError.notConnectedError("Connector is not initialized");
113120
const chainConfig = this.coreOptions.chains.find((x) => x.chainId === chainId);
114121
if (!chainConfig) throw WalletLoginError.connectionError("Chain config is not available");
122+
123+
// Skip tracking for injected MetaMask since it's handled in connectTo
124+
// Skip tracking for rehydration since only new connections are tracked
125+
// Only track non-injected MetaMask when connection completes since it auto-initializes to generate QR code
126+
const shouldTrack = !this.isInjected && !this.rehydrated;
127+
const startTime = Date.now();
128+
const eventData = {
129+
connector: this.name,
130+
connector_type: this.type,
131+
is_injected: this.isInjected,
132+
chain_id: getCaipChainId(chainConfig),
133+
chain_name: chainConfig?.displayName,
134+
chain_namespace: chainConfig?.chainNamespace,
135+
};
136+
115137
try {
116138
if (this.status !== CONNECTOR_STATUS.CONNECTING) {
117139
this.status = CONNECTOR_STATUS.CONNECTING;
@@ -144,6 +166,16 @@ class MetaMaskConnector extends BaseEvmConnector<void> {
144166
});
145167

146168
this.status = CONNECTOR_STATUS.CONNECTED;
169+
170+
// track connection events
171+
if (shouldTrack) {
172+
this.analytics?.track(ANALYTICS_EVENTS.CONNECTION_STARTED, eventData);
173+
this.analytics?.track(ANALYTICS_EVENTS.CONNECTION_COMPLETED, {
174+
...eventData,
175+
duration: Date.now() - startTime,
176+
});
177+
}
178+
147179
this.emit(CONNECTOR_EVENTS.CONNECTED, {
148180
connector: WALLET_CONNECTORS.METAMASK,
149181
reconnected: this.rehydrated,
@@ -155,6 +187,16 @@ class MetaMaskConnector extends BaseEvmConnector<void> {
155187
this.status = CONNECTOR_STATUS.READY;
156188
if (!this.rehydrated) this.emit(CONNECTOR_EVENTS.ERRORED, error as Web3AuthError);
157189
this.rehydrated = false;
190+
191+
// track connection events
192+
if (shouldTrack) {
193+
this.analytics?.track(ANALYTICS_EVENTS.CONNECTION_STARTED, eventData);
194+
this.analytics?.track(ANALYTICS_EVENTS.CONNECTION_COMPLETED, {
195+
...eventData,
196+
...getErrorAnalyticsProperties(error),
197+
duration: Date.now() - startTime,
198+
});
199+
}
158200
if (error instanceof Web3AuthError) throw error;
159201
throw WalletLoginError.connectionError("Failed to login with MetaMask wallet", error);
160202
}
@@ -229,7 +271,7 @@ class MetaMaskConnector extends BaseEvmConnector<void> {
229271
}
230272

231273
export const metaMaskConnector = (params?: Partial<MetaMaskSDKOptions>): ConnectorFn => {
232-
return ({ coreOptions }: ConnectorParams) => {
233-
return new MetaMaskConnector({ connectorSettings: params, coreOptions });
274+
return ({ coreOptions, analytics }: ConnectorParams) => {
275+
return new MetaMaskConnector({ connectorSettings: params, coreOptions, analytics });
234276
};
235277
};

packages/no-modal/src/connectors/wallet-connect-v2-connector/walletConnectV2Connector.ts

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
import { signChallenge, verifySignedChallenge } from "@toruslabs/base-controllers";
1+
import { getErrorAnalyticsProperties, signChallenge, verifySignedChallenge } from "@toruslabs/base-controllers";
22
import Client from "@walletconnect/sign-client";
33
import { SessionTypes } from "@walletconnect/types";
44
import { getSdkError, isValidArray } from "@walletconnect/utils";
55
import { EVM_METHOD_TYPES, SOLANA_METHOD_TYPES } from "@web3auth/ws-embed";
66
import deepmerge from "deepmerge";
77

88
import {
9+
type Analytics,
10+
ANALYTICS_EVENTS,
911
BaseConnector,
1012
BaseConnectorLoginParams,
1113
CHAIN_NAMESPACES,
@@ -23,6 +25,7 @@ import {
2325
ConnectorNamespaceType,
2426
ConnectorParams,
2527
CustomChainConfig,
28+
getCaipChainId,
2629
getSavedToken,
2730
IdentityTokenInfo,
2831
IProvider,
@@ -61,6 +64,8 @@ class WalletConnectV2Connector extends BaseConnector<void> {
6164

6265
private wcProvider: WalletConnectV2Provider | null = null;
6366

67+
private analytics?: Analytics;
68+
6469
constructor(options: WalletConnectV2ConnectorOptions) {
6570
super(options);
6671
this.connectorOptions = { ...options };
@@ -72,6 +77,8 @@ class WalletConnectV2Connector extends BaseConnector<void> {
7277
loginSettings: this.connectorOptions?.loginSettings ?? {},
7378
};
7479

80+
this.analytics = options.analytics;
81+
7582
if (qrcodeModal) this.connectorOptions.connectorSettings.qrcodeModal = qrcodeModal;
7683
if (walletConnectInitOptions)
7784
this.connectorOptions.connectorSettings.walletConnectInitOptions = {
@@ -151,16 +158,41 @@ class WalletConnectV2Connector extends BaseConnector<void> {
151158
if (!chainConfig) throw WalletLoginError.connectionError("Chain config is not available");
152159
if (!this.connector) throw WalletInitializationError.notReady("Wallet connector is not ready yet");
153160

161+
// Skip tracking for rehydration since only new connections are tracked
162+
// Track when connection completes since it auto-initializes to generate QR code
163+
const shouldTrack = !this.connected && !this.rehydrated;
164+
const startTime = Date.now();
165+
const eventData = {
166+
connector: this.name,
167+
connector_type: this.type,
168+
is_injected: this.isInjected,
169+
chain_id: getCaipChainId(chainConfig),
170+
chain_name: chainConfig?.displayName,
171+
chain_namespace: chainConfig?.chainNamespace,
172+
};
173+
154174
try {
175+
const trackCompletionEvents = () => {
176+
// track connection events
177+
if (shouldTrack) {
178+
this.analytics?.track(ANALYTICS_EVENTS.CONNECTION_STARTED, eventData);
179+
this.analytics?.track(ANALYTICS_EVENTS.CONNECTION_COMPLETED, {
180+
...eventData,
181+
duration: Date.now() - startTime,
182+
});
183+
}
184+
};
185+
155186
// if already connected
156187
if (this.connected) {
157188
await this.onConnectHandler({ chain: chainConfig });
158189
return this.provider;
159190
}
160191

161192
if (this.status !== CONNECTOR_STATUS.CONNECTING) {
162-
await this.createNewSession({ chainConfig });
193+
await this.createNewSession({ chainConfig, trackCompletionEvents });
163194
}
195+
164196
return this.provider;
165197
} catch (error) {
166198
log.error("Wallet connect v2 connector error while connecting", error);
@@ -169,6 +201,16 @@ class WalletConnectV2Connector extends BaseConnector<void> {
169201
this.rehydrated = true;
170202
this.emit(CONNECTOR_EVENTS.ERRORED, error as Web3AuthError);
171203

204+
// track connection events
205+
if (shouldTrack) {
206+
this.analytics?.track(ANALYTICS_EVENTS.CONNECTION_STARTED, eventData);
207+
this.analytics?.track(ANALYTICS_EVENTS.CONNECTION_COMPLETED, {
208+
...eventData,
209+
...getErrorAnalyticsProperties(error),
210+
duration: Date.now() - startTime,
211+
});
212+
}
213+
172214
const finalError =
173215
error instanceof Web3AuthError
174216
? error
@@ -293,9 +335,11 @@ class WalletConnectV2Connector extends BaseConnector<void> {
293335
private async createNewSession({
294336
forceNewSession = false,
295337
chainConfig,
338+
trackCompletionEvents,
296339
}: {
297340
forceNewSession?: boolean;
298341
chainConfig: CustomChainConfig;
342+
trackCompletionEvents?: () => void;
299343
}): Promise<void> {
300344
try {
301345
if (!this.connector) throw WalletInitializationError.notReady("Wallet connector is not ready yet");
@@ -334,7 +378,7 @@ class WalletConnectV2Connector extends BaseConnector<void> {
334378
const session = await approval();
335379
this.activeSession = session;
336380
// Handle the returned session (e.g. update UI to "connected" state).
337-
await this.onConnectHandler({ chain: chainConfig });
381+
await this.onConnectHandler({ chain: chainConfig, trackCompletionEvents });
338382
if (qrcodeModal) {
339383
qrcodeModal.closeModal();
340384
}
@@ -357,7 +401,7 @@ class WalletConnectV2Connector extends BaseConnector<void> {
357401
}
358402
}
359403

360-
private async onConnectHandler({ chain }: { chain: CustomChainConfig }) {
404+
private async onConnectHandler({ chain, trackCompletionEvents }: { chain: CustomChainConfig; trackCompletionEvents?: () => void }) {
361405
if (!this.connector || !this.wcProvider) throw WalletInitializationError.notReady("Wallet connect connector is not ready yet");
362406
this.subscribeEvents();
363407
if (this.connectorOptions.connectorSettings?.qrcodeModal) {
@@ -369,6 +413,10 @@ class WalletConnectV2Connector extends BaseConnector<void> {
369413
await this.wcProvider.setupProvider(this.connector);
370414
this.cleanupPendingPairings();
371415
this.status = CONNECTOR_STATUS.CONNECTED;
416+
417+
// track connection events
418+
if (trackCompletionEvents) trackCompletionEvents();
419+
372420
this.emit(CONNECTOR_EVENTS.CONNECTED, {
373421
connector: WALLET_CONNECTORS.WALLET_CONNECT_V2,
374422
reconnected: this.rehydrated,
@@ -405,7 +453,7 @@ class WalletConnectV2Connector extends BaseConnector<void> {
405453
}
406454

407455
export const walletConnectV2Connector = (params?: IConnectorSettings): ConnectorFn => {
408-
return ({ coreOptions, projectConfig }: ConnectorParams) => {
456+
return ({ coreOptions, projectConfig, analytics }: ConnectorParams) => {
409457
const projectId = params?.walletConnectInitOptions?.projectId || projectConfig?.walletConnectProjectId;
410458

411459
const connectorSettings = {
@@ -416,6 +464,7 @@ export const walletConnectV2Connector = (params?: IConnectorSettings): Connector
416464
return new WalletConnectV2Connector({
417465
connectorSettings,
418466
coreOptions,
467+
analytics,
419468
});
420469
};
421470
};

packages/no-modal/src/noModal.ts

Lines changed: 11 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ export class Web3AuthNoModal extends SafeEventEmitter<Web3AuthNoModalEvents> imp
314314
connector: connectorName,
315315
connector_type: connector.type,
316316
chain_id: getCaipChainId(initialChain),
317+
chain_name: initialChain.displayName,
317318
chain_namespace: initialChain.chainNamespace,
318319
auth_connection: authLoginParams.authConnection,
319320
auth_connection_id: authLoginParams.authConnectionId,
@@ -334,6 +335,7 @@ export class Web3AuthNoModal extends SafeEventEmitter<Web3AuthNoModalEvents> imp
334335
connector_type: connector.type,
335336
is_injected: connector.isInjected,
336337
chain_id: getCaipChainId(initialChain),
338+
chain_name: initialChain.displayName,
337339
chain_namespace: initialChain.chainNamespace,
338340
};
339341
}
@@ -342,13 +344,17 @@ export class Web3AuthNoModal extends SafeEventEmitter<Web3AuthNoModalEvents> imp
342344
this.analytics.track(ANALYTICS_EVENTS.CONNECTION_STARTED, eventData);
343345

344346
return new Promise((resolve, reject) => {
347+
const cleanup = () => {
348+
this.off(CONNECTOR_EVENTS.CONNECTED, onConnected);
349+
this.off(CONNECTOR_EVENTS.ERRORED, onErrored);
350+
};
345351
const onConnected = async () => {
346352
// track connection completed event
347353
this.analytics.track(ANALYTICS_EVENTS.CONNECTION_COMPLETED, {
348354
...eventData,
349355
duration: Date.now() - startTime,
350356
});
351-
this.off(CONNECTOR_EVENTS.ERRORED, onErrored);
357+
cleanup();
352358
resolve(this.provider);
353359
};
354360
const onErrored = async (err: Web3AuthError) => {
@@ -358,7 +364,7 @@ export class Web3AuthNoModal extends SafeEventEmitter<Web3AuthNoModalEvents> imp
358364
...getErrorAnalyticsProperties(err),
359365
duration: Date.now() - startTime,
360366
});
361-
this.off(CONNECTOR_EVENTS.CONNECTED, onConnected);
367+
cleanup();
362368
reject(err);
363369
};
364370
this.once(CONNECTOR_EVENTS.CONNECTED, onConnected);
@@ -615,8 +621,10 @@ export class Web3AuthNoModal extends SafeEventEmitter<Web3AuthNoModalEvents> imp
615621
const rpcHostnames = Array.from(new Set(this.coreOptions.chains?.map((chain) => getHostname(chain.rpcTarget)))).filter(Boolean);
616622
return {
617623
chain_ids: this.coreOptions.chains?.map((chain) => getCaipChainId(chain)),
624+
chain_names: this.coreOptions.chains?.map((chain) => chain.displayName),
618625
chain_rpc_targets: rpcHostnames,
619626
default_chain_id: defaultChain ? getCaipChainId(defaultChain) : undefined,
627+
default_chain_name: defaultChain?.displayName,
620628
logging_enabled: this.coreOptions.enableLogging,
621629
storage_type: this.coreOptions.storageType,
622630
session_time: this.coreOptions.sessionTime,
@@ -665,6 +673,7 @@ export class Web3AuthNoModal extends SafeEventEmitter<Web3AuthNoModalEvents> imp
665673
const config = {
666674
projectConfig,
667675
coreOptions: this.coreOptions,
676+
analytics: this.analytics,
668677
};
669678

670679
// add injected connectors
@@ -769,22 +778,6 @@ export class Web3AuthNoModal extends SafeEventEmitter<Web3AuthNoModalEvents> imp
769778
}
770779
}
771780

772-
// WalletConnect and non-injected MetaMask is auto-connect in background to generate QR code URI without interfering with user's selected connection
773-
// We need to track their events if they're not rehydrated
774-
const isConnectionAutoStartedInBackground =
775-
connector.name === WALLET_CONNECTORS.WALLET_CONNECT_V2 || (connector.name === WALLET_CONNECTORS.METAMASK && !connector.isInjected);
776-
if (isConnectionAutoStartedInBackground && this.cachedConnector !== connector.name) {
777-
const eventData = {
778-
connector: connector.name,
779-
connector_type: connector.type,
780-
is_injected: connector.isInjected,
781-
chain_id: getCaipChainId(this.currentChain),
782-
chain_namespace: this.currentChain?.chainNamespace,
783-
};
784-
this.analytics.track(ANALYTICS_EVENTS.CONNECTION_STARTED, eventData);
785-
this.analytics.track(ANALYTICS_EVENTS.CONNECTION_COMPLETED, eventData);
786-
}
787-
788781
let finalProvider = (provider as IBaseProvider<unknown>).provider || (provider as SafeEventEmitterProvider);
789782

790783
// setup AA provider if AA is enabled
@@ -861,23 +854,6 @@ export class Web3AuthNoModal extends SafeEventEmitter<Web3AuthNoModalEvents> imp
861854
connector.on(CONNECTOR_EVENTS.ERRORED, (data) => {
862855
this.status = CONNECTOR_STATUS.ERRORED;
863856
this.clearCache();
864-
865-
// WalletConnect and non-injected MetaMask is auto-connect in background to generate QR code URI without interfering with user's selected connection
866-
// We need to track their events if they're not rehydrated
867-
const isConnectionAutoStartedInBackground =
868-
connector.name === WALLET_CONNECTORS.WALLET_CONNECT_V2 || (connector.name === WALLET_CONNECTORS.METAMASK && !connector.isInjected);
869-
if (isConnectionAutoStartedInBackground && this.cachedConnector !== connector.name) {
870-
const eventData = {
871-
connector: connector.name,
872-
connector_type: connector.type,
873-
is_injected: connector.isInjected,
874-
chain_id: getCaipChainId(this.currentChain),
875-
chain_namespace: this.currentChain?.chainNamespace,
876-
};
877-
this.analytics.track(ANALYTICS_EVENTS.CONNECTION_STARTED, eventData);
878-
this.analytics.track(ANALYTICS_EVENTS.CONNECTION_FAILED, eventData);
879-
}
880-
881857
this.emit(CONNECTOR_EVENTS.ERRORED, data, this.loginMode);
882858
log.debug("errored", this.status, this.connectedConnectorName);
883859
});

0 commit comments

Comments
 (0)