@tezos-x/octez.connect-dapp 5.0.4 → 5.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/dapp-client/DAppClient.d.ts +25 -0
- package/dist/cjs/dapp-client/DAppClient.js +145 -39
- package/dist/cjs/dapp-client/DAppClient.js.map +1 -1
- package/dist/cjs/transports/DappP2PTransport.js +1 -0
- package/dist/cjs/transports/DappP2PTransport.js.map +1 -1
- package/dist/cjs/transports/DappPostMessageTransport.js +1 -0
- package/dist/cjs/transports/DappPostMessageTransport.js.map +1 -1
- package/dist/esm/dapp-client/DAppClient.d.ts +25 -0
- package/dist/esm/dapp-client/DAppClient.js +136 -38
- package/dist/esm/dapp-client/DAppClient.js.map +1 -1
- package/dist/esm/transports/DappP2PTransport.js +1 -0
- package/dist/esm/transports/DappP2PTransport.js.map +1 -1
- package/dist/esm/transports/DappPostMessageTransport.js +1 -0
- package/dist/esm/transports/DappPostMessageTransport.js.map +1 -1
- package/dist/octez.connect.dapp.min.js +1 -1
- package/package.json +9 -9
|
@@ -46,6 +46,7 @@ class DappP2PTransport extends octez_connect_transport_matrix_1.P2PTransport {
|
|
|
46
46
|
return __awaiter(this, void 0, void 0, function* () {
|
|
47
47
|
logger.log('stopListeningForNewPeers');
|
|
48
48
|
this.newPeerListener = undefined;
|
|
49
|
+
this.client.clearPendingPairings();
|
|
49
50
|
});
|
|
50
51
|
}
|
|
51
52
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DappP2PTransport.js","sourceRoot":"","sources":["../../../src/transports/DappP2PTransport.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,sEAMqC;AACrC,oEAAoD;AACpD,4FAAsE;AAGtE,MAAM,MAAM,GAAG,IAAI,2BAAM,CAAC,kBAAkB,CAAC,CAAA;AAE7C;;;;GAIG;AACH,MAAa,gBAAiB,SAAQ,6CAGrC;IACC,YACE,IAAY,EACZ,OAAgB,EAChB,OAAgB,EAChB,WAA8B,EAC9B,OAAgB,EAChB,MAAe;QAEf,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,gCAAU,CAAC,wBAAwB,EAAE,OAAO,EAAE,MAAM,CAAC,CAAA;IAClG,CAAC;IAEY,wBAAwB;;YACnC,OAAO,IAAI,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAO,IAAI,EAAE,EAAE;gBACxD,MAAM,CAAC,GAAG,CAAC,kBAAkB,EAAE,eAAe,EAAE,IAAI,CAAC,SAAS,CAAC,CAAA;gBAE/D,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;gBAExB,IAAI,CAAC,YAAY,GAAG,qCAAe,CAAC,SAAS,CAAA;gBAE7C,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;oBACzB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAA;oBAC1B,IAAI,CAAC,eAAe,GAAG,SAAS,CAAA,CAAC,uCAAuC;gBAC1E,CAAC;YACH,CAAC,CAAA,CAAC,CAAA;QACJ,CAAC;KAAA;IAEY,gBAAgB,CAC3B,eAA2D;;YAE3D,MAAM,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAA;YAC9B,IAAI,CAAC,eAAe,GAAG,eAAe,CAAA;QACxC,CAAC;KAAA;IAEY,wBAAwB;;YACnC,MAAM,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAA;YACtC,IAAI,CAAC,eAAe,GAAG,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"DappP2PTransport.js","sourceRoot":"","sources":["../../../src/transports/DappP2PTransport.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,sEAMqC;AACrC,oEAAoD;AACpD,4FAAsE;AAGtE,MAAM,MAAM,GAAG,IAAI,2BAAM,CAAC,kBAAkB,CAAC,CAAA;AAE7C;;;;GAIG;AACH,MAAa,gBAAiB,SAAQ,6CAGrC;IACC,YACE,IAAY,EACZ,OAAgB,EAChB,OAAgB,EAChB,WAA8B,EAC9B,OAAgB,EAChB,MAAe;QAEf,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,gCAAU,CAAC,wBAAwB,EAAE,OAAO,EAAE,MAAM,CAAC,CAAA;IAClG,CAAC;IAEY,wBAAwB;;YACnC,OAAO,IAAI,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAO,IAAI,EAAE,EAAE;gBACxD,MAAM,CAAC,GAAG,CAAC,kBAAkB,EAAE,eAAe,EAAE,IAAI,CAAC,SAAS,CAAC,CAAA;gBAE/D,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;gBAExB,IAAI,CAAC,YAAY,GAAG,qCAAe,CAAC,SAAS,CAAA;gBAE7C,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;oBACzB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAA;oBAC1B,IAAI,CAAC,eAAe,GAAG,SAAS,CAAA,CAAC,uCAAuC;gBAC1E,CAAC;YACH,CAAC,CAAA,CAAC,CAAA;QACJ,CAAC;KAAA;IAEY,gBAAgB,CAC3B,eAA2D;;YAE3D,MAAM,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAA;YAC9B,IAAI,CAAC,eAAe,GAAG,eAAe,CAAA;QACxC,CAAC;KAAA;IAEY,wBAAwB;;YACnC,MAAM,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAA;YACtC,IAAI,CAAC,eAAe,GAAG,SAAS,CAAA;YAChC,IAAI,CAAC,MAAM,CAAC,oBAAoB,EAAE,CAAA;QACpC,CAAC;KAAA;CACF;AA1CD,4CA0CC","sourcesContent":["import {\n Storage,\n StorageKey,\n TransportStatus,\n ExtendedP2PPairingResponse,\n NodeDistributions\n} from '@tezos-x/octez.connect-types'\nimport { Logger } from '@tezos-x/octez.connect-core'\nimport { P2PTransport } from '@tezos-x/octez.connect-transport-matrix'\nimport { KeyPair } from '@tezos-x/octez.connect-utils'\n\nconst logger = new Logger('DappP2PTransport')\n\n/**\n * @internalapi\n *\n *\n */\nexport class DappP2PTransport extends P2PTransport<\n ExtendedP2PPairingResponse,\n StorageKey.TRANSPORT_P2P_PEERS_DAPP\n> {\n constructor(\n name: string,\n keyPair: KeyPair,\n storage: Storage,\n matrixNodes: NodeDistributions,\n iconUrl?: string,\n appUrl?: string\n ) {\n super(name, keyPair, storage, matrixNodes, StorageKey.TRANSPORT_P2P_PEERS_DAPP, iconUrl, appUrl)\n }\n\n public async startOpenChannelListener(): Promise<void> {\n return this.client.listenForChannelOpening(async (peer) => {\n logger.log('listenForNewPeer', `new publicKey`, peer.publicKey)\n\n await this.addPeer(peer)\n\n this._isConnected = TransportStatus.CONNECTED\n\n if (this.newPeerListener) {\n this.newPeerListener(peer)\n this.newPeerListener = undefined // TODO: Remove this once we use the id\n }\n })\n }\n\n public async listenForNewPeer(\n newPeerListener: (peer: ExtendedP2PPairingResponse) => void\n ): Promise<void> {\n logger.log('listenForNewPeer')\n this.newPeerListener = newPeerListener\n }\n\n public async stopListeningForNewPeers(): Promise<void> {\n logger.log('stopListeningForNewPeers')\n this.newPeerListener = undefined\n this.client.clearPendingPairings()\n }\n}\n"]}
|
|
@@ -46,6 +46,7 @@ class DappPostMessageTransport extends octez_connect_transport_postmessage_1.Pos
|
|
|
46
46
|
return __awaiter(this, void 0, void 0, function* () {
|
|
47
47
|
logger.log('stopListeningForNewPeers');
|
|
48
48
|
this.newPeerListener = undefined;
|
|
49
|
+
this.client.clearPendingPairings();
|
|
49
50
|
});
|
|
50
51
|
}
|
|
51
52
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DappPostMessageTransport.js","sourceRoot":"","sources":["../../../src/transports/DappPostMessageTransport.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,sEAKqC;AACrC,oEAAoD;AACpD,sGAAmF;AAGnF,MAAM,MAAM,GAAG,IAAI,2BAAM,CAAC,0BAA0B,CAAC,CAAA;AAErD;;;;GAIG;AACH,MAAa,wBAAyB,SAAQ,0DAG7C;IACC,YAAY,IAAY,EAAE,OAAgB,EAAE,OAAgB;QAC1D,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,gCAAU,CAAC,gCAAgC,CAAC,CAAA;IAC5E,CAAC;IAEY,wBAAwB;;YACnC,OAAO,IAAI,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAO,IAAwC,EAAE,EAAE;gBAC5F,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,qCAAqC,EAAE,IAAI,CAAC,CAAA;gBAElE,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;gBAExB,IAAI,CAAC,YAAY,GAAG,qCAAe,CAAC,SAAS,CAAA;gBAE7C,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;oBACzB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAA;oBAC1B,IAAI,CAAC,eAAe,GAAG,SAAS,CAAA,CAAC,uCAAuC;gBAC1E,CAAC;YACH,CAAC,CAAA,CAAC,CAAA;QACJ,CAAC;KAAA;IAEY,gBAAgB,CAC3B,eAAmE;;YAEnE,MAAM,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAA;YAC9B,IAAI,CAAC,eAAe,GAAG,eAAe,CAAA;QACxC,CAAC;KAAA;IAEY,wBAAwB;;YACnC,MAAM,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAA;YACtC,IAAI,CAAC,eAAe,GAAG,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"DappPostMessageTransport.js","sourceRoot":"","sources":["../../../src/transports/DappPostMessageTransport.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,sEAKqC;AACrC,oEAAoD;AACpD,sGAAmF;AAGnF,MAAM,MAAM,GAAG,IAAI,2BAAM,CAAC,0BAA0B,CAAC,CAAA;AAErD;;;;GAIG;AACH,MAAa,wBAAyB,SAAQ,0DAG7C;IACC,YAAY,IAAY,EAAE,OAAgB,EAAE,OAAgB;QAC1D,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,gCAAU,CAAC,gCAAgC,CAAC,CAAA;IAC5E,CAAC;IAEY,wBAAwB;;YACnC,OAAO,IAAI,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAO,IAAwC,EAAE,EAAE;gBAC5F,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,qCAAqC,EAAE,IAAI,CAAC,CAAA;gBAElE,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;gBAExB,IAAI,CAAC,YAAY,GAAG,qCAAe,CAAC,SAAS,CAAA;gBAE7C,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;oBACzB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAA;oBAC1B,IAAI,CAAC,eAAe,GAAG,SAAS,CAAA,CAAC,uCAAuC;gBAC1E,CAAC;YACH,CAAC,CAAA,CAAC,CAAA;QACJ,CAAC;KAAA;IAEY,gBAAgB,CAC3B,eAAmE;;YAEnE,MAAM,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAA;YAC9B,IAAI,CAAC,eAAe,GAAG,eAAe,CAAA;QACxC,CAAC;KAAA;IAEY,wBAAwB;;YACnC,MAAM,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAA;YACtC,IAAI,CAAC,eAAe,GAAG,SAAS,CAAA;YAChC,IAAI,CAAC,MAAM,CAAC,oBAAoB,EAAE,CAAA;QACpC,CAAC;KAAA;CACF;AAnCD,4DAmCC","sourcesContent":["import {\n TransportStatus,\n StorageKey,\n Storage,\n ExtendedPostMessagePairingResponse\n} from '@tezos-x/octez.connect-types'\nimport { Logger } from '@tezos-x/octez.connect-core'\nimport { PostMessageTransport } from '@tezos-x/octez.connect-transport-postmessage'\nimport { KeyPair } from '@tezos-x/octez.connect-utils'\n\nconst logger = new Logger('DappPostMessageTransport')\n\n/**\n * @internalapi\n *\n *\n */\nexport class DappPostMessageTransport extends PostMessageTransport<\n ExtendedPostMessagePairingResponse,\n StorageKey.TRANSPORT_POSTMESSAGE_PEERS_DAPP\n> {\n constructor(name: string, keyPair: KeyPair, storage: Storage) {\n super(name, keyPair, storage, StorageKey.TRANSPORT_POSTMESSAGE_PEERS_DAPP)\n }\n\n public async startOpenChannelListener(): Promise<void> {\n return this.client.listenForChannelOpening(async (peer: ExtendedPostMessagePairingResponse) => {\n logger.log('connect', `received PostMessagePairingResponse`, peer)\n\n await this.addPeer(peer)\n\n this._isConnected = TransportStatus.CONNECTED\n\n if (this.newPeerListener) {\n this.newPeerListener(peer)\n this.newPeerListener = undefined // TODO: Remove this once we use the id\n }\n })\n }\n\n public async listenForNewPeer(\n newPeerListener: (peer: ExtendedPostMessagePairingResponse) => void\n ): Promise<void> {\n logger.log('listenForNewPeer')\n this.newPeerListener = newPeerListener\n }\n\n public async stopListeningForNewPeers(): Promise<void> {\n logger.log('stopListeningForNewPeers')\n this.newPeerListener = undefined\n this.client.clearPendingPairings()\n }\n}\n"]}
|
|
@@ -88,6 +88,18 @@ export declare class DAppClient extends Client {
|
|
|
88
88
|
private prepareRequest;
|
|
89
89
|
private createStateSnapshot;
|
|
90
90
|
private initUserID;
|
|
91
|
+
/**
|
|
92
|
+
* Disconnect the postMessage and P2P transports and drop all three instances.
|
|
93
|
+
*
|
|
94
|
+
* A dropped transport is not garbage. Its communication client keeps its
|
|
95
|
+
* `window` handlers (postMessage) or its matrix sync (P2P) until it is told to
|
|
96
|
+
* stop, so every path that used to just forget the instances left one more
|
|
97
|
+
* live client behind per connection. Every such path goes through here.
|
|
98
|
+
*
|
|
99
|
+
* WalletConnect is left to each call site: its session is shared across tabs
|
|
100
|
+
* and only the leader may close it, which the callers already handle.
|
|
101
|
+
*/
|
|
102
|
+
private dropTransports;
|
|
91
103
|
initInternalTransports(): Promise<void>;
|
|
92
104
|
private initEvents;
|
|
93
105
|
private onRelayerError;
|
|
@@ -302,5 +314,18 @@ export declare class DAppClient extends Client {
|
|
|
302
314
|
private addOpenRequest;
|
|
303
315
|
private sendNotificationWithAccessToken;
|
|
304
316
|
private buildAccountInfosFromV4Fanout;
|
|
317
|
+
/**
|
|
318
|
+
* Is this message from the wallet behind the active account? Over P2P the
|
|
319
|
+
* senderId is already bound to the decrypting key (see handleResponse).
|
|
320
|
+
*/
|
|
321
|
+
private isActiveWallet;
|
|
322
|
+
/** Is this message from a wallet we are paired with on this connection? */
|
|
323
|
+
private isPairedWallet;
|
|
324
|
+
/**
|
|
325
|
+
* A wallet-initiated disconnect. Strangers are ignored; a saved but inactive
|
|
326
|
+
* wallet only removes its own records; only the active wallet (or any paired
|
|
327
|
+
* wallet when no account is active) ends the session.
|
|
328
|
+
*/
|
|
329
|
+
private onUnsolicitedDisconnect;
|
|
305
330
|
private onNewAccount;
|
|
306
331
|
}
|
|
@@ -182,11 +182,25 @@ export class DAppClient extends Client {
|
|
|
182
182
|
// normalized back to those same flat shapes — the wire dialect stays
|
|
183
183
|
// invisible to integrators. Non-Tezos wrapped payloads keep the
|
|
184
184
|
// pass-through of the generic permissionRequest/request API.
|
|
185
|
-
|
|
185
|
+
// The wrapped dialect is identified by its payload, not by the absence of
|
|
186
|
+
// a flat marker: `wrapBeaconMessage` only ever emits
|
|
187
|
+
// { id, version, senderId, message }. Routing on the version alone dropped
|
|
188
|
+
// every flat message stamped with the wrapped dialect's number -- which is
|
|
189
|
+
// how a wallet built outside this SDK (or on <= 4.8.6, echoing the peer's
|
|
190
|
+
// version) sends its `disconnect` -- so handleDisconnect never ran and the
|
|
191
|
+
// dApp kept the account connected until the user disconnected a second
|
|
192
|
+
// time (#52). Testing for `.message` positively routes that message down
|
|
193
|
+
// the flat path, and still reads a non-conformant wallet's wrapped
|
|
194
|
+
// envelope as wrapped even when it carries a redundant top-level `type`.
|
|
195
|
+
const hasWrappedPayload = Boolean(wireMessage.message);
|
|
196
|
+
const isWrapped = usesWrappedMessages(wireMessage.version) && hasWrappedPayload;
|
|
186
197
|
// Issue #33: a V3-versioned message can arrive without its wrapped payload.
|
|
187
|
-
//
|
|
188
|
-
//
|
|
189
|
-
|
|
198
|
+
// With no top-level `type` either it carries nothing to route on, so drop
|
|
199
|
+
// it instead of dereferencing an undefined payload, which would throw an
|
|
200
|
+
// unhandled rejection inside the transport subscription callback.
|
|
201
|
+
if (usesWrappedMessages(wireMessage.version) &&
|
|
202
|
+
!hasWrappedPayload &&
|
|
203
|
+
!('type' in wireMessage)) {
|
|
190
204
|
logger.log('handleResponse', 'Received wrapped message with undefined payload; dropping', wireMessage);
|
|
191
205
|
return;
|
|
192
206
|
}
|
|
@@ -195,6 +209,13 @@ export class DAppClient extends Client {
|
|
|
195
209
|
: wireMessage;
|
|
196
210
|
const message = normalized ?? wireMessage;
|
|
197
211
|
const typedMessage = normalized ?? wireMessage.message;
|
|
212
|
+
if ((connectionInfo.origin === Origin.P2P &&
|
|
213
|
+
message.senderId !== (await getSenderId(connectionInfo.id))) ||
|
|
214
|
+
(connectionInfo.origin === Origin.EXTENSION &&
|
|
215
|
+
!(await this.isPairedWallet(message.senderId, connectionInfo)))) {
|
|
216
|
+
logger.warn('handleResponse', 'dropping message: senderId does not match its sender');
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
198
219
|
let appMetadata = normalized
|
|
199
220
|
? typedMessage.appMetadata
|
|
200
221
|
: typedMessage.blockchainData?.appMetadata;
|
|
@@ -242,7 +263,8 @@ export class DAppClient extends Client {
|
|
|
242
263
|
logger.log('handleResponse', 'Received message', message, connectionInfo);
|
|
243
264
|
logger.log('### message ###', JSON.stringify(message));
|
|
244
265
|
logger.log('### connectionInfo ###', connectionInfo);
|
|
245
|
-
|
|
266
|
+
// endSession=false: a saved but inactive wallet removes only its own records.
|
|
267
|
+
const handleDisconnect = async (endSession = true) => {
|
|
246
268
|
this.analytics.track('event', 'DAppClient', 'Disconnect received from Wallet');
|
|
247
269
|
const relevantTransport = connectionInfo.origin === Origin.P2P
|
|
248
270
|
? this.p2pTransport
|
|
@@ -257,11 +279,12 @@ export class DAppClient extends Client {
|
|
|
257
279
|
}
|
|
258
280
|
}
|
|
259
281
|
await this.removeAccountsForPeerIds([message.senderId]);
|
|
282
|
+
if (!endSession) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
260
285
|
await this.events.emit(BeaconEvent.CHANNEL_CLOSED);
|
|
261
286
|
// Reset transport state so next requestPermissions() shows pairing modal
|
|
262
|
-
this.
|
|
263
|
-
this.p2pTransport = undefined;
|
|
264
|
-
this.walletConnectTransport = undefined;
|
|
287
|
+
await this.dropTransports();
|
|
265
288
|
await this.setTransport(undefined);
|
|
266
289
|
await this.setActivePeer(undefined);
|
|
267
290
|
};
|
|
@@ -308,10 +331,15 @@ export class DAppClient extends Client {
|
|
|
308
331
|
}
|
|
309
332
|
else {
|
|
310
333
|
if (typedMessage.type === BeaconMessageType.Disconnect) {
|
|
311
|
-
await handleDisconnect
|
|
334
|
+
await this.onUnsolicitedDisconnect(message.senderId, connectionInfo, handleDisconnect);
|
|
312
335
|
}
|
|
313
336
|
else if (typedMessage.type === BeaconMessageType.ChangeAccountRequest) {
|
|
314
|
-
await this.
|
|
337
|
+
if (await this.isActiveWallet(message.senderId, connectionInfo)) {
|
|
338
|
+
await this.onNewAccount(typedMessage, connectionInfo);
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
logger.warn('handleResponse', 'ignoring change account from a non-active wallet');
|
|
342
|
+
}
|
|
315
343
|
}
|
|
316
344
|
}
|
|
317
345
|
if (this._transport.isResolved()) {
|
|
@@ -444,6 +472,28 @@ export class DAppClient extends Client {
|
|
|
444
472
|
this.userId = await generateGUID();
|
|
445
473
|
this.storage.set(StorageKey.USER_ID, this.userId);
|
|
446
474
|
}
|
|
475
|
+
/**
|
|
476
|
+
* Disconnect the postMessage and P2P transports and drop all three instances.
|
|
477
|
+
*
|
|
478
|
+
* A dropped transport is not garbage. Its communication client keeps its
|
|
479
|
+
* `window` handlers (postMessage) or its matrix sync (P2P) until it is told to
|
|
480
|
+
* stop, so every path that used to just forget the instances left one more
|
|
481
|
+
* live client behind per connection. Every such path goes through here.
|
|
482
|
+
*
|
|
483
|
+
* WalletConnect is left to each call site: its session is shared across tabs
|
|
484
|
+
* and only the leader may close it, which the callers already handle.
|
|
485
|
+
*/
|
|
486
|
+
async dropTransports() {
|
|
487
|
+
const dropped = [this.postMessageTransport, this.p2pTransport].filter((transport) => transport !== undefined);
|
|
488
|
+
this.postMessageTransport = undefined;
|
|
489
|
+
this.p2pTransport = undefined;
|
|
490
|
+
this.walletConnectTransport = undefined;
|
|
491
|
+
// Best effort: a transport that fails to disconnect is still dropped, and
|
|
492
|
+
// the others are still disconnected.
|
|
493
|
+
await Promise.all(dropped.map((transport) => transport.disconnect().catch((error) => {
|
|
494
|
+
logger.warn('dropTransports', error instanceof Error ? error.message : String(error));
|
|
495
|
+
})));
|
|
496
|
+
}
|
|
447
497
|
async initInternalTransports() {
|
|
448
498
|
const seed = await this.storage.get(StorageKey.BEACON_SDK_SECRET_SEED);
|
|
449
499
|
if (!seed) {
|
|
@@ -542,6 +592,7 @@ export class DAppClient extends Client {
|
|
|
542
592
|
*/
|
|
543
593
|
async destroy() {
|
|
544
594
|
await this.createStateSnapshot();
|
|
595
|
+
await this.dropTransports();
|
|
545
596
|
await super.destroy();
|
|
546
597
|
}
|
|
547
598
|
async init(transport, substratePairing) {
|
|
@@ -669,13 +720,19 @@ export class DAppClient extends Client {
|
|
|
669
720
|
});
|
|
670
721
|
const abortHandler = async () => {
|
|
671
722
|
logger.log('init', 'ABORTED');
|
|
723
|
+
// Popup closed: replies to the QR code / request it showed no longer count.
|
|
724
|
+
stopListening();
|
|
672
725
|
this.sendMetrics('performance-metrics/save', await this.buildPayload('connect', 'abort'));
|
|
673
726
|
await Promise.all([
|
|
674
|
-
|
|
675
|
-
//
|
|
676
|
-
|
|
727
|
+
walletConnectTransport?.disconnect(),
|
|
728
|
+
// Disconnects postMessage and P2P -- `postMessageTransport` above
|
|
729
|
+
// is this.postMessageTransport, so dropTransports() already owns
|
|
730
|
+
// it -- and forgets all three. It also stops the P2P client
|
|
731
|
+
// started for the pairing QR: if its start is still pending,
|
|
732
|
+
// P2PCommunicationClient.start() notices the stop once the login
|
|
733
|
+
// completes and shuts it down.
|
|
734
|
+
this.dropTransports()
|
|
677
735
|
]);
|
|
678
|
-
this.postMessageTransport = this.walletConnectTransport = this.p2pTransport = undefined;
|
|
679
736
|
this._activeAccount.isResolved() && this.clearActiveAccount();
|
|
680
737
|
this.events.emit(BeaconEvent.PAIR_ABORTED).catch((emitError) => console.warn(emitError));
|
|
681
738
|
if (this._initPromiseReject) {
|
|
@@ -751,11 +808,8 @@ export class DAppClient extends Client {
|
|
|
751
808
|
this.storage.set(StorageKey.ACTIVE_ACCOUNT, undefined);
|
|
752
809
|
emit && this.events.emit(BeaconEvent.INVALID_ACTIVE_ACCOUNT_STATE);
|
|
753
810
|
!emit && this.hideUI(['alert']);
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
this.walletConnectTransport?.disconnect()
|
|
757
|
-
]);
|
|
758
|
-
this.postMessageTransport = this.p2pTransport = this.walletConnectTransport = undefined;
|
|
811
|
+
const walletConnectTransport = this.walletConnectTransport;
|
|
812
|
+
await Promise.all([walletConnectTransport?.disconnect(), this.dropTransports()]);
|
|
759
813
|
await this.setActivePeer(undefined);
|
|
760
814
|
await this.setTransport(undefined);
|
|
761
815
|
this._initPromise = undefined;
|
|
@@ -787,7 +841,7 @@ export class DAppClient extends Client {
|
|
|
787
841
|
if (!this.debounceSetActiveAccount && transport instanceof WalletConnectTransport) {
|
|
788
842
|
this.debounceSetActiveAccount = true;
|
|
789
843
|
this._initPromise = undefined;
|
|
790
|
-
|
|
844
|
+
await this.dropTransports();
|
|
791
845
|
if (this.multiTabChannel.isLeader() || isMobileOS(window)) {
|
|
792
846
|
await transport.disconnect();
|
|
793
847
|
this.openRequestsOtherTabs.clear();
|
|
@@ -1750,9 +1804,7 @@ export class DAppClient extends Client {
|
|
|
1750
1804
|
if (request.type === BeaconMessageType.PermissionRequest &&
|
|
1751
1805
|
(await this.getActiveAccount()) === undefined) {
|
|
1752
1806
|
this._initPromise = undefined;
|
|
1753
|
-
this.
|
|
1754
|
-
this.p2pTransport = undefined;
|
|
1755
|
-
this.walletConnectTransport = undefined;
|
|
1807
|
+
await this.dropTransports();
|
|
1756
1808
|
await this.setTransport();
|
|
1757
1809
|
await this.setActivePeer();
|
|
1758
1810
|
}
|
|
@@ -2359,22 +2411,11 @@ export class DAppClient extends Client {
|
|
|
2359
2411
|
}
|
|
2360
2412
|
await this.clearActiveAccount();
|
|
2361
2413
|
if (!(transport instanceof WalletConnectTransport)) {
|
|
2362
|
-
|
|
2363
|
-
await transport.disconnect();
|
|
2364
|
-
}
|
|
2365
|
-
catch (disconnectError) {
|
|
2366
|
-
const message = disconnectError instanceof Error ? disconnectError.message : String(disconnectError);
|
|
2367
|
-
if (typeof message === 'string' && message.includes('Syncing stopped manually')) {
|
|
2368
|
-
logger.log('disconnect', 'Matrix sync stopped manually');
|
|
2369
|
-
}
|
|
2370
|
-
else {
|
|
2371
|
-
throw disconnectError;
|
|
2372
|
-
}
|
|
2373
|
-
}
|
|
2414
|
+
await transport.disconnect();
|
|
2374
2415
|
}
|
|
2375
|
-
this
|
|
2376
|
-
|
|
2377
|
-
this.
|
|
2416
|
+
// The active transport is already disconnected above; this stops the
|
|
2417
|
+
// other one (a second disconnect is a no-op) and drops the instances.
|
|
2418
|
+
await this.dropTransports();
|
|
2378
2419
|
await this.setTransport();
|
|
2379
2420
|
this._initPromise = undefined;
|
|
2380
2421
|
this._initPromiseReject = undefined;
|
|
@@ -2500,6 +2541,54 @@ export class DAppClient extends Client {
|
|
|
2500
2541
|
};
|
|
2501
2542
|
}));
|
|
2502
2543
|
}
|
|
2544
|
+
/**
|
|
2545
|
+
* Is this message from the wallet behind the active account? Over P2P the
|
|
2546
|
+
* senderId is already bound to the decrypting key (see handleResponse).
|
|
2547
|
+
*/
|
|
2548
|
+
async isActiveWallet(senderId, connectionInfo) {
|
|
2549
|
+
if (connectionInfo.origin === Origin.WALLETCONNECT) {
|
|
2550
|
+
return true;
|
|
2551
|
+
}
|
|
2552
|
+
await this.activeAccountLoaded;
|
|
2553
|
+
const active = await this.getActiveAccount();
|
|
2554
|
+
return Boolean(active &&
|
|
2555
|
+
active.senderId === senderId &&
|
|
2556
|
+
active.origin.type === connectionInfo.origin &&
|
|
2557
|
+
active.origin.id === connectionInfo.id);
|
|
2558
|
+
}
|
|
2559
|
+
/** Is this message from a wallet we are paired with on this connection? */
|
|
2560
|
+
async isPairedWallet(senderId, connectionInfo) {
|
|
2561
|
+
if (connectionInfo.origin === Origin.P2P) {
|
|
2562
|
+
const peers = (await this.storage.get(StorageKey.TRANSPORT_P2P_PEERS_DAPP)) ?? [];
|
|
2563
|
+
return peers.some((peer) => peer.publicKey === connectionInfo.id);
|
|
2564
|
+
}
|
|
2565
|
+
if (connectionInfo.origin === Origin.EXTENSION) {
|
|
2566
|
+
const peers = (await this.storage.get(StorageKey.TRANSPORT_POSTMESSAGE_PEERS_DAPP)) ?? [];
|
|
2567
|
+
for (const peer of peers) {
|
|
2568
|
+
if (peer.extensionId === connectionInfo.id &&
|
|
2569
|
+
(await getSenderId(peer.publicKey)) === senderId) {
|
|
2570
|
+
return true;
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
return false;
|
|
2574
|
+
}
|
|
2575
|
+
return true; // WalletConnect: unchanged, see isActiveWallet
|
|
2576
|
+
}
|
|
2577
|
+
/**
|
|
2578
|
+
* A wallet-initiated disconnect. Strangers are ignored; a saved but inactive
|
|
2579
|
+
* wallet only removes its own records; only the active wallet (or any paired
|
|
2580
|
+
* wallet when no account is active) ends the session.
|
|
2581
|
+
*/
|
|
2582
|
+
async onUnsolicitedDisconnect(senderId, connectionInfo, handleDisconnect) {
|
|
2583
|
+
if (!(await this.isPairedWallet(senderId, connectionInfo))) {
|
|
2584
|
+
logger.warn('handleResponse', 'ignoring disconnect from an unknown wallet');
|
|
2585
|
+
return;
|
|
2586
|
+
}
|
|
2587
|
+
await this.activeAccountLoaded;
|
|
2588
|
+
const active = await this.getActiveAccount();
|
|
2589
|
+
const endSession = !active || (await this.isActiveWallet(senderId, connectionInfo));
|
|
2590
|
+
await handleDisconnect(endSession);
|
|
2591
|
+
}
|
|
2503
2592
|
async onNewAccount(message, connectionInfo) {
|
|
2504
2593
|
// TODO: Migration code. Remove sometime after 1.0.0 release.
|
|
2505
2594
|
const tempPK = message.publicKey || message.pubkey || message.pubKey;
|
|
@@ -2508,6 +2597,15 @@ export class DAppClient extends Client {
|
|
|
2508
2597
|
throw new Error('PublicKey or Address must be defined');
|
|
2509
2598
|
}
|
|
2510
2599
|
const address = message.address ?? (await getAddressFromPublicKey(publicKey));
|
|
2600
|
+
if (connectionInfo.origin !== Origin.WALLETCONNECT &&
|
|
2601
|
+
publicKey &&
|
|
2602
|
+
message.address &&
|
|
2603
|
+
message.walletType !== 'abstracted_account') {
|
|
2604
|
+
const derived = await getAddressFromPublicKey(publicKey).catch(() => undefined);
|
|
2605
|
+
if (derived && derived !== message.address) {
|
|
2606
|
+
throw new Error(`Address "${message.address}" does not match its public key`);
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2511
2609
|
if (!isValidAddress(address)) {
|
|
2512
2610
|
throw new Error(`Invalid address: "${address}"`);
|
|
2513
2611
|
}
|