Skip to content
This repository was archived by the owner on Dec 3, 2025. It is now read-only.

Commit 8456cbc

Browse files
authored
chore: add more vigilant checking to resolution (#101)
* chore: add more vigilant checking to resolution * chore: update blocking for getNotificationHistory * chore: run prettier * chore: remove unused import * chore: add UT * chore: run prettier * chore: update * fix: bug where correct topic is ignored * chore: use response topic instead of subscription topic * chore: bump patch
1 parent ad99fac commit 8456cbc

5 files changed

Lines changed: 164 additions & 14 deletions

File tree

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/notify-client/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@walletconnect/notify-client",
33
"description": "WalletConnect Notify Client",
4-
"version": "1.1.1",
4+
"version": "1.1.2",
55
"author": "WalletConnect, Inc. <walletconnect.com>",
66
"homepage": "https://github.com/walletconnect/notify-client-js/",
77
"license": "Apache-2.0",

packages/notify-client/src/controllers/engine.ts

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@ import {
3434
NOTIFY_AUTHORIZATION_STATEMENT_ALL_DOMAINS,
3535
NOTIFY_AUTHORIZATION_STATEMENT_THIS_DOMAIN,
3636
} from "../constants";
37-
import { INotifyEngine, JsonRpcTypes, NotifyClientTypes } from "../types";
37+
import {
38+
INotifyEngine,
39+
JsonRpcTypes,
40+
NotifyClientTypes,
41+
NotifyEngineTypes,
42+
} from "../types";
3843
import { getCaip10FromDidPkh } from "../utils/address";
3944
import { getDappUrl } from "../utils/formats";
4045

@@ -287,13 +292,22 @@ export class NotifyEngine extends INotifyEngine {
287292
);
288293

289294
return new Promise<boolean>((resolve) => {
290-
this.client.once("notify_subscription", (args) => {
295+
const listener = (
296+
args: NotifyClientTypes.EventArguments["notify_subscription"]
297+
) => {
298+
if (args.topic !== responseTopic) {
299+
return;
300+
}
301+
this.client.off("notify_subscription", listener);
291302
if (args.params.error) {
292303
resolve(false);
293304
} else {
294305
resolve(true);
295306
}
296-
});
307+
};
308+
309+
this.client.on("notify_subscription", listener);
310+
297311
// SPEC: Wallet sends wc_notifySubscribe request (type 1 envelope) on subscribe topic with subscriptionAuth
298312
this.sendRequest<"wc_notifySubscribe">(
299313
subscribeTopic,
@@ -350,13 +364,22 @@ export class NotifyEngine extends INotifyEngine {
350364
);
351365

352366
return new Promise<boolean>((resolve, reject) => {
353-
this.client.once("notify_update", (args) => {
367+
const listener = (
368+
args: NotifyClientTypes.EventArguments["notify_update"]
369+
) => {
370+
if (args.topic !== topic) {
371+
return;
372+
}
373+
this.client.off("notify_update", listener);
374+
354375
if (args.params.error) {
355376
reject(args.params.error);
356377
} else {
357378
resolve(true);
358379
}
359-
});
380+
};
381+
382+
this.client.on("notify_update", listener);
360383

361384
this.sendRequest(topic, "wc_notifyUpdate", {
362385
updateAuth,
@@ -420,13 +443,23 @@ export class NotifyEngine extends INotifyEngine {
420443
);
421444

422445
return new Promise((resolve, reject) => {
423-
this.once("notify_get_notifications_response", (args) => {
446+
const listener = (
447+
args: NotifyEngineTypes.EventArguments["notify_get_notifications_response"]
448+
) => {
449+
if (args.topic !== topic) {
450+
return;
451+
}
452+
453+
this.off("notify_get_notifications_response", listener);
454+
424455
if (args.error === null) {
425456
resolve(args);
426457
} else {
427458
reject(new Error(args.error));
428459
}
429-
});
460+
};
461+
462+
this.on("notify_get_notifications_response", listener);
430463

431464
// Add timeout to prevent memory leaks with unresolving promises
432465
setTimeout(() => {
@@ -490,13 +523,21 @@ export class NotifyEngine extends INotifyEngine {
490523
});
491524

492525
return new Promise<void>((resolve, reject) => {
493-
this.client.once("notify_delete", (args) => {
526+
const listener = (
527+
args: NotifyClientTypes.EventArguments["notify_delete"]
528+
) => {
529+
if (args.topic !== topic) {
530+
return;
531+
}
532+
this.client.off("notify_delete", listener);
494533
if (args.params.error) {
495534
reject(args.params.error);
496535
} else {
497536
resolve();
498537
}
499-
});
538+
};
539+
540+
this.client.on("notify_delete", listener);
500541

501542
this.sendRequest(topic, "wc_notifyDelete", { deleteAuth }).then(() => {
502543
this.client.logger.info(
@@ -941,6 +982,7 @@ export class NotifyEngine extends INotifyEngine {
941982
}));
942983

943984
this.emit("notify_get_notifications_response", {
985+
topic,
944986
hasMore: claims.mre ?? false,
945987
hasMoreUnread: claims.mur ?? false,
946988
error: null,
@@ -954,6 +996,7 @@ export class NotifyEngine extends INotifyEngine {
954996
);
955997

956998
this.emit("notify_get_notifications_response", {
999+
topic,
9571000
error: payload.error.message,
9581001
});
9591002
}

packages/notify-client/src/types/engine.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@ export declare namespace NotifyEngineTypes {
2525
publishedAt: number;
2626
}
2727

28-
type EventResponseOrError<T> =
28+
type EventResponseOrError<T> = { topic: string } & (
2929
| (T & { error: null })
3030
| {
3131
error: string;
32-
};
32+
}
33+
);
3334

3435
type Event = "notify_get_notifications_response";
3536

packages/notify-client/test/clients.spec.ts

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
NotifyClientTypes,
1414
} from "../src/";
1515
import { waitForEvent } from "./helpers/async";
16-
import { testDappMetadata } from "./helpers/mocks";
16+
import { gmHackersMetadata, testDappMetadata } from "./helpers/mocks";
1717
import { createNotifySubscription, sendNotifyMessage } from "./helpers/notify";
1818
import { disconnectSocket } from "./helpers/ws";
1919
import axios from "axios";
@@ -844,6 +844,112 @@ describe("Notify", () => {
844844
});
845845
});
846846

847+
describe("Blocking functions", () => {
848+
it("Subscribe only resolves once a subscription succeeded and is stored", async () => {
849+
const preparedRegistration = await wallet.prepareRegistration({
850+
account,
851+
domain: testDappMetadata.appDomain,
852+
allApps: true,
853+
});
854+
855+
await wallet.register({
856+
registerParams: preparedRegistration.registerParams,
857+
signature: await onSign(preparedRegistration.message),
858+
});
859+
860+
await wallet.subscribe({
861+
appDomain: testDappMetadata.appDomain,
862+
account,
863+
});
864+
865+
expect(wallet.subscriptions.length).toEqual(1);
866+
expect(wallet.subscriptions.getAll()[0].metadata.appDomain).toEqual(
867+
testDappMetadata.appDomain
868+
);
869+
});
870+
871+
it.skipIf(!hasTestProjectSecret)(
872+
"Only resolves when topic accurate response is issued",
873+
async () => {
874+
const preparedRegistration = await wallet.prepareRegistration({
875+
account,
876+
domain: testDappMetadata.appDomain,
877+
allApps: true,
878+
});
879+
880+
await wallet.register({
881+
registerParams: preparedRegistration.registerParams,
882+
signature: await onSign(preparedRegistration.message),
883+
});
884+
885+
await wallet.subscribe({
886+
appDomain: testDappMetadata.appDomain,
887+
account,
888+
});
889+
890+
expect(wallet.subscriptions.length).toEqual(1);
891+
expect(wallet.subscriptions.getAll()[0].metadata.appDomain).toEqual(
892+
testDappMetadata.appDomain
893+
);
894+
895+
const app1Topic = wallet.subscriptions.getAll()[0].topic;
896+
897+
let gotMessage = false;
898+
899+
// send messages to app1
900+
await sendNotifyMessage(account, "Test1");
901+
902+
console.log(">>>>>>>>>> sent message");
903+
904+
wallet.on("notify_message", () => {
905+
gotMessage = true;
906+
});
907+
908+
await waitForEvent(() => gotMessage);
909+
910+
console.log(">>>>>>>>>> got message");
911+
912+
const notifs1 = wallet.getNotificationHistory({
913+
topic: app1Topic,
914+
limit: 5,
915+
});
916+
917+
// close transport to prevent getting a real response from the relay
918+
await wallet.core.relayer.transportClose();
919+
920+
const emptyNotif = {
921+
body: "",
922+
id: "",
923+
sentAt: Date.now(),
924+
title: "",
925+
type: "",
926+
url: "",
927+
};
928+
929+
wallet.engine["emit"]("notify_get_notifications_response", {
930+
topic: "wrong_topic",
931+
error: null,
932+
hasMore: false,
933+
hasMoreUnread: false,
934+
notifications: [],
935+
});
936+
937+
wallet.engine["emit"]("notify_get_notifications_response", {
938+
topic: app1Topic,
939+
error: null,
940+
hasMore: false,
941+
hasMoreUnread: false,
942+
notifications: [emptyNotif, emptyNotif],
943+
});
944+
945+
expect(notifs1).resolves.toSatisfy((resolved: any) => {
946+
console.log("Resolved is!", resolved);
947+
return resolved.notifications.length === 2;
948+
});
949+
}
950+
);
951+
});
952+
847953
describe.skipIf(!hasTestProjectSecret)("Message Deduping", () => {
848954
it("dedups messages based on notify message id", async () => {
849955
await createNotifySubscription(wallet, account, onSign);

0 commit comments

Comments
 (0)