diff --git a/packages/core/src/controllers/events.ts b/packages/core/src/controllers/events.ts index fd073c832..a16ccb193 100644 --- a/packages/core/src/controllers/events.ts +++ b/packages/core/src/controllers/events.ts @@ -1,6 +1,6 @@ import { generateChildLogger, Logger } from "@walletconnect/logger"; import { ICore, IEventClient, EventClientTypes } from "@walletconnect/types"; -import { uuidv4 } from "@walletconnect/utils"; +import { formatUA, isTestRun, uuidv4 } from "@walletconnect/utils"; import { CORE_STORAGE_PREFIX, EVENTS_CLIENT_API_URL, @@ -16,12 +16,12 @@ export class EventClient extends IEventClient { public readonly context = EVENTS_STORAGE_CONTEXT; private readonly storagePrefix = CORE_STORAGE_PREFIX; private readonly storageVersion = EVENTS_STORAGE_VERSION; - private events = new Map(); private shouldPersist = false; constructor(public core: ICore, public logger: Logger, telemetryEnabled = true) { super(core, logger, telemetryEnabled); this.logger = generateChildLogger(logger, this.context); + this.telemetryEnabled = telemetryEnabled; if (telemetryEnabled) { this.restore().then(async () => { await this.submit(); @@ -39,6 +39,31 @@ export class EventClient extends IEventClient { ); } + public init: IEventClient["init"] = async () => { + if (isTestRun()) return; + try { + const initEvent = { + eventId: uuidv4(), + timestamp: Date.now(), + props: { + event: "INIT", + type: "", + properties: { + client_id: await this.core.crypto.getClientId(), + user_agent: formatUA( + this.core.relayer.protocol, + this.core.relayer.version, + RELAYER_SDK_VERSION, + ), + }, + }, + }; + await this.sendEvent([initEvent] as unknown as EventClientTypes.Event[]); + } catch (error) { + this.logger.warn(error); + } + }; + public createEvent: IEventClient["createEvent"] = (params) => { const { event = "ERROR", @@ -172,13 +197,7 @@ export class EventClient extends IEventClient { if (eventsToSend.length === 0) return; try { - const response = await fetch( - `${EVENTS_CLIENT_API_URL}?projectId=${this.core.projectId}&st=events_sdk&sv=js-${RELAYER_SDK_VERSION}`, - { - method: "POST", - body: JSON.stringify(eventsToSend), - }, - ); + const response = await this.sendEvent(eventsToSend); if (response.ok) { for (const event of eventsToSend) { this.events.delete(event.eventId); @@ -189,4 +208,15 @@ export class EventClient extends IEventClient { this.logger.warn(error); } }; + + private sendEvent = async (events: EventClientTypes.Event[]) => { + const response = await fetch( + `${EVENTS_CLIENT_API_URL}?projectId=${this.core.projectId}&st=events_sdk&sv=js-${RELAYER_SDK_VERSION}`, + { + method: "POST", + body: JSON.stringify(events), + }, + ); + return response; + }; } diff --git a/packages/core/src/controllers/verify.ts b/packages/core/src/controllers/verify.ts index e748be805..bcc7636e3 100644 --- a/packages/core/src/controllers/verify.ts +++ b/packages/core/src/controllers/verify.ts @@ -1,6 +1,6 @@ import { generateChildLogger, getLoggerContext, Logger } from "@walletconnect/logger"; import { ICore, IVerify } from "@walletconnect/types"; -import { isBrowser, isNode, P256KeyDataType, verifyP256Jwt } from "@walletconnect/utils"; +import { isBrowser, isTestRun, P256KeyDataType, verifyP256Jwt } from "@walletconnect/utils"; import { FIVE_SECONDS, ONE_SECOND, toMiliseconds } from "@walletconnect/time"; import { getDocument } from "@walletconnect/window-getters"; import { decodeJWT } from "@walletconnect/relay-auth"; @@ -40,7 +40,7 @@ export class Verify extends IVerify { super(core, logger, store); this.logger = generateChildLogger(logger, this.name); this.abortController = new AbortController(); - this.isDevEnv = isNode() && process.env.IS_VITEST; + this.isDevEnv = isTestRun(); this.init(); } diff --git a/packages/core/src/core.ts b/packages/core/src/core.ts index feca3dafe..b40394dbf 100644 --- a/packages/core/src/core.ts +++ b/packages/core/src/core.ts @@ -194,6 +194,7 @@ export class Core extends ICore { await this.relayer.init(); await this.heartbeat.init(); await this.pairing.init(); + this.eventClient.init(); this.linkModeSupportedApps = (await this.storage.getItem(WALLETCONNECT_LINK_MODE_APPS)) || []; this.initialized = true; diff --git a/packages/core/test/events.spec.ts b/packages/core/test/events.spec.ts index c5928f709..82666786e 100644 --- a/packages/core/test/events.spec.ts +++ b/packages/core/test/events.spec.ts @@ -193,4 +193,26 @@ describe("Events Client", () => { // @ts-expect-error - accessing private properties expect(core.eventClient.events.size).toBe(0); }); + + it("should send init event", async () => { + process.env.IS_VITEST = false as any; + const core = new Core({ ...TEST_CORE_OPTIONS, telemetryEnabled: false }); + let initCalled = false; + // @ts-expect-error - accessing private properties + core.eventClient.sendEvent = async (payload: any) => { + initCalled = true; + expect(payload).toBeDefined(); + expect(payload.length).to.eql(1); + expect(payload[0].props.event).to.eql("INIT"); + expect(payload[0].props.properties.client_id).to.eql(await core.crypto.getClientId()); + }; + await core.start(); + await new Promise((resolve) => setTimeout(resolve, 500)); + + if (!initCalled) { + throw new Error("init not called"); + } + + process.env.IS_VITEST = true as any; + }); }); diff --git a/packages/sign-client/src/controllers/engine.ts b/packages/sign-client/src/controllers/engine.ts index c9fce87b0..fc84f6072 100644 --- a/packages/sign-client/src/controllers/engine.ts +++ b/packages/sign-client/src/controllers/engine.ts @@ -96,6 +96,7 @@ import { BASE64URL, getSearchParamFromURL, isReactNative, + isTestRun, } from "@walletconnect/utils"; import EventEmmiter from "events"; import { @@ -2914,10 +2915,7 @@ export class Engine extends IEngine { }; private registerLinkModeListeners = async () => { - if ( - (typeof process !== "undefined" && process.env.IS_VITEST) || - (isReactNative() && this.client.metadata.redirect?.linkMode) - ) { + if (isTestRun() || (isReactNative() && this.client.metadata.redirect?.linkMode)) { const linking = (global as any)?.Linking; // global.Linking is set by react-native-compat if (typeof linking !== "undefined") { diff --git a/packages/types/src/core/events.ts b/packages/types/src/core/events.ts index 271c01153..d82699544 100644 --- a/packages/types/src/core/events.ts +++ b/packages/types/src/core/events.ts @@ -30,6 +30,8 @@ export abstract class IEventClient { constructor(public core: ICore, public logger: Logger, public telemetryEnabled: boolean) {} + public abstract init(): Promise; + public abstract createEvent(params: { event?: "ERROR"; type?: string; diff --git a/packages/types/src/core/relayer.ts b/packages/types/src/core/relayer.ts index d74afa33a..d55051ba3 100644 --- a/packages/types/src/core/relayer.ts +++ b/packages/types/src/core/relayer.ts @@ -78,6 +78,10 @@ export interface RelayerClientMetadata { } export abstract class IRelayer extends IEvents { + public abstract protocol: string; + + public abstract version: number; + public abstract core: ICore; public abstract logger: Logger; diff --git a/packages/utils/src/misc.ts b/packages/utils/src/misc.ts index a352cbae1..77e0f96e8 100644 --- a/packages/utils/src/misc.ts +++ b/packages/utils/src/misc.ts @@ -441,3 +441,7 @@ export function uuidv4() { return v.toString(16); }); } + +export function isTestRun() { + return typeof process !== "undefined" && process.env.IS_VITEST === "true"; +}