Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: decrypted notifications #3997

Merged
merged 14 commits into from
Dec 19, 2023
2 changes: 2 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@
"@walletconnect/utils": "2.10.6",
"events": "^3.3.0",
"lodash.isequal": "4.5.0",
"uint8arrays": "^3.1.0"
"uint8arrays": "^3.1.0",
"isomorphic-unfetch": "3.1.0"
},
"devDependencies": {
"@types/lodash.isequal": "4.5.6",
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/constants/echo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const ECHO_CONTEXT = "echo";

export const ECHO_URL = "https://echo.walletconnect.com";
1 change: 1 addition & 0 deletions packages/core/src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export * from "./pairing";
export * from "./history";
export * from "./expirer";
export * from "./verify";
export * from "./echo";
31 changes: 31 additions & 0 deletions packages/core/src/controllers/echo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { generateChildLogger, Logger } from "@walletconnect/logger";
import { IEchoClient } from "@walletconnect/types";
import { ECHO_CONTEXT, ECHO_URL } from "../constants";
import fetch from "isomorphic-unfetch";

export class EchoClient extends IEchoClient {
public readonly context = ECHO_CONTEXT;
constructor(public projectId: string, public logger: Logger) {
super(projectId, logger);
this.logger = generateChildLogger(logger, this.context);
}

public registerDeviceToken: IEchoClient["registerDeviceToken"] = async (params) => {
const { clientId, token, notificationType, enableEncrypted = false } = params;

const echoUrl = `${ECHO_URL}/${this.projectId}/clients`;

await fetch(echoUrl, {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we need to use isomorphic-unfetch or something here? Or is it already polyfilled

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm actually kind of surprised this hasn't come up before here where there's already a fetch call: https://github.com/WalletConnect/walletconnect-monorepo/blob/v2.0/packages/core/src/controllers/verify.ts#L103

Is the Verify controller called only in browsers?

method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
client_id: clientId,
type: notificationType,
token,
always_raw: enableEncrypted,
}),
});
};
}
1 change: 1 addition & 0 deletions packages/core/src/controllers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export * from "./pairing";
export * from "./history";
export * from "./expirer";
export * from "./verify";
export * from "./echo";
12 changes: 11 additions & 1 deletion packages/core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,15 @@ import {
} from "@walletconnect/logger";
import { CoreTypes, ICore } from "@walletconnect/types";

import { Crypto, Relayer, Pairing, JsonRpcHistory, Expirer, Verify } from "./controllers";
import {
Crypto,
Relayer,
Pairing,
JsonRpcHistory,
Expirer,
Verify,
EchoClient,
} from "./controllers";
import {
CORE_CONTEXT,
CORE_DEFAULT,
Expand Down Expand Up @@ -39,6 +47,7 @@ export class Core extends ICore {
public expirer: ICore["expirer"];
public pairing: ICore["pairing"];
public verify: ICore["verify"];
public echoClient: ICore["echoClient"];

private initialized = false;

Expand Down Expand Up @@ -77,6 +86,7 @@ export class Core extends ICore {
});
this.pairing = new Pairing(this, this.logger);
this.verify = new Verify(this.projectId || "", this.logger);
this.echoClient = new EchoClient(this.projectId || "", this.logger);
}

get context() {
Expand Down
3 changes: 2 additions & 1 deletion packages/sign-client/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { SignClient as Client } from "./client";

import { Session } from "./controllers/session";
export * from "./constants";

export const SessionStore = Session;
export const SignClient = Client;
export default Client;
2 changes: 2 additions & 0 deletions packages/types/src/core/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { IExpirer } from "./expirer";
import { IPairing } from "./pairing";
import { Logger } from "@walletconnect/logger";
import { IVerify } from "./verify";
import { IEchoClient } from "./echo";
export declare namespace CoreTypes {
interface Options {
projectId?: string;
Expand Down Expand Up @@ -54,6 +55,7 @@ export abstract class ICore extends IEvents {
public abstract expirer: IExpirer;
public abstract pairing: IPairing;
public abstract verify: IVerify;
public abstract echoClient: IEchoClient;

constructor(public opts?: CoreTypes.Options) {
super();
Expand Down
18 changes: 18 additions & 0 deletions packages/types/src/core/echo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Logger } from "@walletconnect/logger";

export declare namespace EchoClientTypes {
type RegisterDeviceTokenParams = {
clientId: string;
token: string;
notificationType: "fcm" | "apns" | "apns-sandbox" | "noop";
enableEncrypted?: boolean;
};
}
export abstract class IEchoClient {
public abstract readonly context: string;
constructor(public projectId: string, public logger: Logger) {}

public abstract registerDeviceToken(
params: EchoClientTypes.RegisterDeviceTokenParams,
): Promise<void>;
}
1 change: 1 addition & 0 deletions packages/types/src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export * from "./keychain";
export * from "./expirer";
export * from "./pairing";
export * from "./verify";
export * from "./echo";
11 changes: 11 additions & 0 deletions packages/web3wallet/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import EventEmitter from "events";
import { CLIENT_CONTEXT } from "./constants";
import { Engine } from "./controllers";
import { IWeb3Wallet, Web3WalletTypes } from "./types";
import { Notifications } from "./utils";

export class Web3Wallet extends IWeb3Wallet {
public name: IWeb3Wallet["name"];
Expand All @@ -10,6 +11,7 @@ export class Web3Wallet extends IWeb3Wallet {
public events: IWeb3Wallet["events"] = new EventEmitter();
public engine: IWeb3Wallet["engine"];
public metadata: IWeb3Wallet["metadata"];
public static notifications: Web3WalletTypes.INotifications = Notifications;

static async init(opts: Web3WalletTypes.Options) {
const client = new Web3Wallet(opts);
Expand Down Expand Up @@ -173,6 +175,15 @@ export class Web3Wallet extends IWeb3Wallet {
}
};

public registerDeviceToken: IWeb3Wallet["registerDeviceToken"] = (params) => {
try {
return this.engine.registerDeviceToken(params);
} catch (error: any) {
this.logger.error(error.message);
throw error;
}
};

// ---------- Private ----------------------------------------------- //

private async initialize() {
Expand Down
5 changes: 5 additions & 0 deletions packages/web3wallet/src/controllers/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ export class Engine extends IWeb3WalletEngine {
return this.authClient.formatMessage(params, iss);
};

// Push //
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Push //
// ---------- Push -------------------------------------------------- //

public registerDeviceToken: IWeb3WalletEngine["registerDeviceToken"] = (params) => {
return this.client.core.echoClient.registerDeviceToken(params);
};

// ---------- Private ----------------------------------------------- //

private onSessionRequest = (event: Web3WalletTypes.SessionRequest) => {
Expand Down
21 changes: 19 additions & 2 deletions packages/web3wallet/src/types/client.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import EventEmmiter, { EventEmitter } from "events";
import { ICore, ProposalTypes, Verify } from "@walletconnect/types";
import { ICore, CoreTypes, ProposalTypes, Verify } from "@walletconnect/types";
import { AuthClientTypes } from "@walletconnect/auth-client";
import { IWeb3WalletEngine } from "./engine";
import { Logger } from "@walletconnect/logger";
import { JsonRpcPayload } from "@walletconnect/jsonrpc-utils";

export declare namespace Web3WalletTypes {
type Event = "session_proposal" | "session_request" | "session_delete" | "auth_request";
Expand Down Expand Up @@ -41,7 +42,21 @@ export declare namespace Web3WalletTypes {
name?: string;
}

type Metadata = AuthClientTypes.Metadata;
type Metadata = CoreTypes.Metadata;
bkrem marked this conversation as resolved.
Show resolved Hide resolved

interface INotifications {
decryptMessage: (params: {
topic: string;
encryptedMessage: string;
storageOptions?: CoreTypes.Options["storageOptions"];
storage?: CoreTypes.Options["storage"];
}) => Promise<JsonRpcPayload>;
getMetadata: (params: {
topic: string;
storageOptions?: CoreTypes.Options["storageOptions"];
storage?: CoreTypes.Options["storage"];
}) => Promise<CoreTypes.Metadata>;
}
}

export abstract class IWeb3WalletEvents extends EventEmmiter {
Expand Down Expand Up @@ -104,6 +119,8 @@ export abstract class IWeb3Wallet {
public abstract respondAuthRequest: IWeb3WalletEngine["respondAuthRequest"];
public abstract getPendingAuthRequests: IWeb3WalletEngine["getPendingAuthRequests"];
public abstract formatMessage: IWeb3WalletEngine["formatMessage"];
// push
public abstract registerDeviceToken: IWeb3WalletEngine["registerDeviceToken"];

// ---------- Event Handlers ----------------------------------------------- //
public abstract on: <E extends Web3WalletTypes.Event>(
Expand Down
6 changes: 6 additions & 0 deletions packages/web3wallet/src/types/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
PendingRequestTypes,
ProposalTypes,
SessionTypes,
EchoClientTypes,
} from "@walletconnect/types";
import { IWeb3Wallet } from "./client";

Expand Down Expand Up @@ -83,4 +84,9 @@ export abstract class IWeb3WalletEngine {

// format payload to message string
public abstract formatMessage(payload: AuthEngineTypes.CacaoRequestPayload, iss: string): string;

// ---------- Push ------------------------------------------------- //
public abstract registerDeviceToken(
params: EchoClientTypes.RegisterDeviceTokenParams,
): Promise<void>;
}
1 change: 1 addition & 0 deletions packages/web3wallet/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./notifications";
34 changes: 34 additions & 0 deletions packages/web3wallet/src/utils/notifications.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Core } from "@walletconnect/core";
import { Web3WalletTypes } from "../types";
import { SessionStore } from "@walletconnect/sign-client";

export const Notifications: Web3WalletTypes.INotifications = {
decryptMessage: async (params) => {
const instance = {
core: new Core({
storageOptions: params.storageOptions,
storage: params.storage,
}),
} as any;
await instance.core.crypto.init();
const decoded = instance.core.crypto.decode(params.topic, params.encryptedMessage);
instance.core = null;
return decoded;
},
getMetadata: async (params) => {
ganchoradkov marked this conversation as resolved.
Show resolved Hide resolved
const instances = {
core: new Core({
storageOptions: params.storageOptions,
storage: params.storage,
}),
sessionStore: null,
} as any;
instances.sessionStore = new SessionStore(instances.core, instances.core.logger);
await instances.sessionStore.init();
const session = instances.sessionStore.get(params.topic);
const metadata = session?.peer.metadata;
instances.core = null;
instances.sessionStore = null;
return metadata;
},
};
Loading