@tezos-x/octez.connect-dapp 5.0.3 → 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 +10 -10
|
@@ -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
|
}
|
|
@@ -157,11 +157,25 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
157
157
|
// normalized back to those same flat shapes — the wire dialect stays
|
|
158
158
|
// invisible to integrators. Non-Tezos wrapped payloads keep the
|
|
159
159
|
// pass-through of the generic permissionRequest/request API.
|
|
160
|
-
|
|
160
|
+
// The wrapped dialect is identified by its payload, not by the absence of
|
|
161
|
+
// a flat marker: `wrapBeaconMessage` only ever emits
|
|
162
|
+
// { id, version, senderId, message }. Routing on the version alone dropped
|
|
163
|
+
// every flat message stamped with the wrapped dialect's number -- which is
|
|
164
|
+
// how a wallet built outside this SDK (or on <= 4.8.6, echoing the peer's
|
|
165
|
+
// version) sends its `disconnect` -- so handleDisconnect never ran and the
|
|
166
|
+
// dApp kept the account connected until the user disconnected a second
|
|
167
|
+
// time (#52). Testing for `.message` positively routes that message down
|
|
168
|
+
// the flat path, and still reads a non-conformant wallet's wrapped
|
|
169
|
+
// envelope as wrapped even when it carries a redundant top-level `type`.
|
|
170
|
+
const hasWrappedPayload = Boolean(wireMessage.message);
|
|
171
|
+
const isWrapped = (0, octez_connect_core_1.usesWrappedMessages)(wireMessage.version) && hasWrappedPayload;
|
|
161
172
|
// Issue #33: a V3-versioned message can arrive without its wrapped payload.
|
|
162
|
-
//
|
|
163
|
-
//
|
|
164
|
-
|
|
173
|
+
// With no top-level `type` either it carries nothing to route on, so drop
|
|
174
|
+
// it instead of dereferencing an undefined payload, which would throw an
|
|
175
|
+
// unhandled rejection inside the transport subscription callback.
|
|
176
|
+
if ((0, octez_connect_core_1.usesWrappedMessages)(wireMessage.version) &&
|
|
177
|
+
!hasWrappedPayload &&
|
|
178
|
+
!('type' in wireMessage)) {
|
|
165
179
|
logger.log('handleResponse', 'Received wrapped message with undefined payload; dropping', wireMessage);
|
|
166
180
|
return;
|
|
167
181
|
}
|
|
@@ -170,6 +184,13 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
170
184
|
: wireMessage;
|
|
171
185
|
const message = normalized !== null && normalized !== void 0 ? normalized : wireMessage;
|
|
172
186
|
const typedMessage = normalized !== null && normalized !== void 0 ? normalized : wireMessage.message;
|
|
187
|
+
if ((connectionInfo.origin === octez_connect_types_1.Origin.P2P &&
|
|
188
|
+
message.senderId !== (yield (0, octez_connect_core_1.getSenderId)(connectionInfo.id))) ||
|
|
189
|
+
(connectionInfo.origin === octez_connect_types_1.Origin.EXTENSION &&
|
|
190
|
+
!(yield this.isPairedWallet(message.senderId, connectionInfo)))) {
|
|
191
|
+
logger.warn('handleResponse', 'dropping message: senderId does not match its sender');
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
173
194
|
let appMetadata = normalized
|
|
174
195
|
? typedMessage.appMetadata
|
|
175
196
|
: (_a = typedMessage.blockchainData) === null || _a === void 0 ? void 0 : _a.appMetadata;
|
|
@@ -217,7 +238,8 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
217
238
|
logger.log('handleResponse', 'Received message', message, connectionInfo);
|
|
218
239
|
logger.log('### message ###', JSON.stringify(message));
|
|
219
240
|
logger.log('### connectionInfo ###', connectionInfo);
|
|
220
|
-
|
|
241
|
+
// endSession=false: a saved but inactive wallet removes only its own records.
|
|
242
|
+
const handleDisconnect = (...args_1) => __awaiter(this, [...args_1], void 0, function* (endSession = true) {
|
|
221
243
|
var _a;
|
|
222
244
|
this.analytics.track('event', 'DAppClient', 'Disconnect received from Wallet');
|
|
223
245
|
const relevantTransport = connectionInfo.origin === octez_connect_types_1.Origin.P2P
|
|
@@ -233,11 +255,12 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
233
255
|
}
|
|
234
256
|
}
|
|
235
257
|
yield this.removeAccountsForPeerIds([message.senderId]);
|
|
258
|
+
if (!endSession) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
236
261
|
yield this.events.emit(events_1.BeaconEvent.CHANNEL_CLOSED);
|
|
237
262
|
// Reset transport state so next requestPermissions() shows pairing modal
|
|
238
|
-
this.
|
|
239
|
-
this.p2pTransport = undefined;
|
|
240
|
-
this.walletConnectTransport = undefined;
|
|
263
|
+
yield this.dropTransports();
|
|
241
264
|
yield this.setTransport(undefined);
|
|
242
265
|
yield this.setActivePeer(undefined);
|
|
243
266
|
});
|
|
@@ -284,10 +307,15 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
284
307
|
}
|
|
285
308
|
else {
|
|
286
309
|
if (typedMessage.type === octez_connect_types_1.BeaconMessageType.Disconnect) {
|
|
287
|
-
yield handleDisconnect
|
|
310
|
+
yield this.onUnsolicitedDisconnect(message.senderId, connectionInfo, handleDisconnect);
|
|
288
311
|
}
|
|
289
312
|
else if (typedMessage.type === octez_connect_types_1.BeaconMessageType.ChangeAccountRequest) {
|
|
290
|
-
yield this.
|
|
313
|
+
if (yield this.isActiveWallet(message.senderId, connectionInfo)) {
|
|
314
|
+
yield this.onNewAccount(typedMessage, connectionInfo);
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
logger.warn('handleResponse', 'ignoring change account from a non-active wallet');
|
|
318
|
+
}
|
|
291
319
|
}
|
|
292
320
|
}
|
|
293
321
|
if (this._transport.isResolved()) {
|
|
@@ -433,6 +461,30 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
433
461
|
this.storage.set(octez_connect_types_1.StorageKey.USER_ID, this.userId);
|
|
434
462
|
});
|
|
435
463
|
}
|
|
464
|
+
/**
|
|
465
|
+
* Disconnect the postMessage and P2P transports and drop all three instances.
|
|
466
|
+
*
|
|
467
|
+
* A dropped transport is not garbage. Its communication client keeps its
|
|
468
|
+
* `window` handlers (postMessage) or its matrix sync (P2P) until it is told to
|
|
469
|
+
* stop, so every path that used to just forget the instances left one more
|
|
470
|
+
* live client behind per connection. Every such path goes through here.
|
|
471
|
+
*
|
|
472
|
+
* WalletConnect is left to each call site: its session is shared across tabs
|
|
473
|
+
* and only the leader may close it, which the callers already handle.
|
|
474
|
+
*/
|
|
475
|
+
dropTransports() {
|
|
476
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
477
|
+
const dropped = [this.postMessageTransport, this.p2pTransport].filter((transport) => transport !== undefined);
|
|
478
|
+
this.postMessageTransport = undefined;
|
|
479
|
+
this.p2pTransport = undefined;
|
|
480
|
+
this.walletConnectTransport = undefined;
|
|
481
|
+
// Best effort: a transport that fails to disconnect is still dropped, and
|
|
482
|
+
// the others are still disconnected.
|
|
483
|
+
yield Promise.all(dropped.map((transport) => transport.disconnect().catch((error) => {
|
|
484
|
+
logger.warn('dropTransports', error instanceof Error ? error.message : String(error));
|
|
485
|
+
})));
|
|
486
|
+
});
|
|
487
|
+
}
|
|
436
488
|
initInternalTransports() {
|
|
437
489
|
return __awaiter(this, void 0, void 0, function* () {
|
|
438
490
|
var _a, _b;
|
|
@@ -546,6 +598,7 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
546
598
|
});
|
|
547
599
|
return __awaiter(this, void 0, void 0, function* () {
|
|
548
600
|
yield this.createStateSnapshot();
|
|
601
|
+
yield this.dropTransports();
|
|
549
602
|
yield _super.destroy.call(this);
|
|
550
603
|
});
|
|
551
604
|
}
|
|
@@ -676,13 +729,19 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
676
729
|
});
|
|
677
730
|
const abortHandler = () => __awaiter(this, void 0, void 0, function* () {
|
|
678
731
|
logger.log('init', 'ABORTED');
|
|
732
|
+
// Popup closed: replies to the QR code / request it showed no longer count.
|
|
733
|
+
stopListening();
|
|
679
734
|
this.sendMetrics('performance-metrics/save', yield this.buildPayload('connect', 'abort'));
|
|
680
735
|
yield Promise.all([
|
|
681
|
-
|
|
682
|
-
//
|
|
683
|
-
|
|
736
|
+
walletConnectTransport === null || walletConnectTransport === void 0 ? void 0 : walletConnectTransport.disconnect(),
|
|
737
|
+
// Disconnects postMessage and P2P -- `postMessageTransport` above
|
|
738
|
+
// is this.postMessageTransport, so dropTransports() already owns
|
|
739
|
+
// it -- and forgets all three. It also stops the P2P client
|
|
740
|
+
// started for the pairing QR: if its start is still pending,
|
|
741
|
+
// P2PCommunicationClient.start() notices the stop once the login
|
|
742
|
+
// completes and shuts it down.
|
|
743
|
+
this.dropTransports()
|
|
684
744
|
]);
|
|
685
|
-
this.postMessageTransport = this.walletConnectTransport = this.p2pTransport = undefined;
|
|
686
745
|
this._activeAccount.isResolved() && this.clearActiveAccount();
|
|
687
746
|
this.events.emit(events_1.BeaconEvent.PAIR_ABORTED).catch((emitError) => console.warn(emitError));
|
|
688
747
|
if (this._initPromiseReject) {
|
|
@@ -761,17 +820,13 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
761
820
|
}
|
|
762
821
|
resetInvalidState() {
|
|
763
822
|
return __awaiter(this, arguments, void 0, function* (emit = true) {
|
|
764
|
-
var _a, _b;
|
|
765
823
|
this.accountManager.removeAllAccounts();
|
|
766
824
|
this._activeAccount = octez_connect_utils_1.ExposedPromise.resolve(undefined);
|
|
767
825
|
this.storage.set(octez_connect_types_1.StorageKey.ACTIVE_ACCOUNT, undefined);
|
|
768
826
|
emit && this.events.emit(events_1.BeaconEvent.INVALID_ACTIVE_ACCOUNT_STATE);
|
|
769
827
|
!emit && this.hideUI(['alert']);
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
(_b = this.walletConnectTransport) === null || _b === void 0 ? void 0 : _b.disconnect()
|
|
773
|
-
]);
|
|
774
|
-
this.postMessageTransport = this.p2pTransport = this.walletConnectTransport = undefined;
|
|
828
|
+
const walletConnectTransport = this.walletConnectTransport;
|
|
829
|
+
yield Promise.all([walletConnectTransport === null || walletConnectTransport === void 0 ? void 0 : walletConnectTransport.disconnect(), this.dropTransports()]);
|
|
775
830
|
yield this.setActivePeer(undefined);
|
|
776
831
|
yield this.setTransport(undefined);
|
|
777
832
|
this._initPromise = undefined;
|
|
@@ -805,7 +860,7 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
805
860
|
if (!this.debounceSetActiveAccount && transport instanceof octez_connect_transport_walletconnect_1.WalletConnectTransport) {
|
|
806
861
|
this.debounceSetActiveAccount = true;
|
|
807
862
|
this._initPromise = undefined;
|
|
808
|
-
|
|
863
|
+
yield this.dropTransports();
|
|
809
864
|
if (this.multiTabChannel.isLeader() || (0, octez_connect_ui_1.isMobileOS)(window)) {
|
|
810
865
|
yield transport.disconnect();
|
|
811
866
|
this.openRequestsOtherTabs.clear();
|
|
@@ -1825,9 +1880,7 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
1825
1880
|
if (request.type === octez_connect_types_1.BeaconMessageType.PermissionRequest &&
|
|
1826
1881
|
(yield this.getActiveAccount()) === undefined) {
|
|
1827
1882
|
this._initPromise = undefined;
|
|
1828
|
-
this.
|
|
1829
|
-
this.p2pTransport = undefined;
|
|
1830
|
-
this.walletConnectTransport = undefined;
|
|
1883
|
+
yield this.dropTransports();
|
|
1831
1884
|
yield this.setTransport();
|
|
1832
1885
|
yield this.setActivePeer();
|
|
1833
1886
|
}
|
|
@@ -2414,22 +2467,11 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
2414
2467
|
}
|
|
2415
2468
|
yield this.clearActiveAccount();
|
|
2416
2469
|
if (!(transport instanceof octez_connect_transport_walletconnect_1.WalletConnectTransport)) {
|
|
2417
|
-
|
|
2418
|
-
yield transport.disconnect();
|
|
2419
|
-
}
|
|
2420
|
-
catch (disconnectError) {
|
|
2421
|
-
const message = disconnectError instanceof Error ? disconnectError.message : String(disconnectError);
|
|
2422
|
-
if (typeof message === 'string' && message.includes('Syncing stopped manually')) {
|
|
2423
|
-
logger.log('disconnect', 'Matrix sync stopped manually');
|
|
2424
|
-
}
|
|
2425
|
-
else {
|
|
2426
|
-
throw disconnectError;
|
|
2427
|
-
}
|
|
2428
|
-
}
|
|
2470
|
+
yield transport.disconnect();
|
|
2429
2471
|
}
|
|
2430
|
-
this
|
|
2431
|
-
|
|
2432
|
-
this.
|
|
2472
|
+
// The active transport is already disconnected above; this stops the
|
|
2473
|
+
// other one (a second disconnect is a no-op) and drops the instances.
|
|
2474
|
+
yield this.dropTransports();
|
|
2433
2475
|
yield this.setTransport();
|
|
2434
2476
|
this._initPromise = undefined;
|
|
2435
2477
|
this._initPromiseReject = undefined;
|
|
@@ -2545,6 +2587,61 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
2545
2587
|
})));
|
|
2546
2588
|
});
|
|
2547
2589
|
}
|
|
2590
|
+
/**
|
|
2591
|
+
* Is this message from the wallet behind the active account? Over P2P the
|
|
2592
|
+
* senderId is already bound to the decrypting key (see handleResponse).
|
|
2593
|
+
*/
|
|
2594
|
+
isActiveWallet(senderId, connectionInfo) {
|
|
2595
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2596
|
+
if (connectionInfo.origin === octez_connect_types_1.Origin.WALLETCONNECT) {
|
|
2597
|
+
return true;
|
|
2598
|
+
}
|
|
2599
|
+
yield this.activeAccountLoaded;
|
|
2600
|
+
const active = yield this.getActiveAccount();
|
|
2601
|
+
return Boolean(active &&
|
|
2602
|
+
active.senderId === senderId &&
|
|
2603
|
+
active.origin.type === connectionInfo.origin &&
|
|
2604
|
+
active.origin.id === connectionInfo.id);
|
|
2605
|
+
});
|
|
2606
|
+
}
|
|
2607
|
+
/** Is this message from a wallet we are paired with on this connection? */
|
|
2608
|
+
isPairedWallet(senderId, connectionInfo) {
|
|
2609
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2610
|
+
var _a, _b;
|
|
2611
|
+
if (connectionInfo.origin === octez_connect_types_1.Origin.P2P) {
|
|
2612
|
+
const peers = (_a = (yield this.storage.get(octez_connect_types_1.StorageKey.TRANSPORT_P2P_PEERS_DAPP))) !== null && _a !== void 0 ? _a : [];
|
|
2613
|
+
return peers.some((peer) => peer.publicKey === connectionInfo.id);
|
|
2614
|
+
}
|
|
2615
|
+
if (connectionInfo.origin === octez_connect_types_1.Origin.EXTENSION) {
|
|
2616
|
+
const peers = (_b = (yield this.storage.get(octez_connect_types_1.StorageKey.TRANSPORT_POSTMESSAGE_PEERS_DAPP))) !== null && _b !== void 0 ? _b : [];
|
|
2617
|
+
for (const peer of peers) {
|
|
2618
|
+
if (peer.extensionId === connectionInfo.id &&
|
|
2619
|
+
(yield (0, octez_connect_core_1.getSenderId)(peer.publicKey)) === senderId) {
|
|
2620
|
+
return true;
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
return false;
|
|
2624
|
+
}
|
|
2625
|
+
return true; // WalletConnect: unchanged, see isActiveWallet
|
|
2626
|
+
});
|
|
2627
|
+
}
|
|
2628
|
+
/**
|
|
2629
|
+
* A wallet-initiated disconnect. Strangers are ignored; a saved but inactive
|
|
2630
|
+
* wallet only removes its own records; only the active wallet (or any paired
|
|
2631
|
+
* wallet when no account is active) ends the session.
|
|
2632
|
+
*/
|
|
2633
|
+
onUnsolicitedDisconnect(senderId, connectionInfo, handleDisconnect) {
|
|
2634
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2635
|
+
if (!(yield this.isPairedWallet(senderId, connectionInfo))) {
|
|
2636
|
+
logger.warn('handleResponse', 'ignoring disconnect from an unknown wallet');
|
|
2637
|
+
return;
|
|
2638
|
+
}
|
|
2639
|
+
yield this.activeAccountLoaded;
|
|
2640
|
+
const active = yield this.getActiveAccount();
|
|
2641
|
+
const endSession = !active || (yield this.isActiveWallet(senderId, connectionInfo));
|
|
2642
|
+
yield handleDisconnect(endSession);
|
|
2643
|
+
});
|
|
2644
|
+
}
|
|
2548
2645
|
onNewAccount(message, connectionInfo) {
|
|
2549
2646
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2550
2647
|
var _a, _b, _c;
|
|
@@ -2555,6 +2652,15 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
2555
2652
|
throw new Error('PublicKey or Address must be defined');
|
|
2556
2653
|
}
|
|
2557
2654
|
const address = (_a = message.address) !== null && _a !== void 0 ? _a : (yield (0, octez_connect_utils_1.getAddressFromPublicKey)(publicKey));
|
|
2655
|
+
if (connectionInfo.origin !== octez_connect_types_1.Origin.WALLETCONNECT &&
|
|
2656
|
+
publicKey &&
|
|
2657
|
+
message.address &&
|
|
2658
|
+
message.walletType !== 'abstracted_account') {
|
|
2659
|
+
const derived = yield (0, octez_connect_utils_1.getAddressFromPublicKey)(publicKey).catch(() => undefined);
|
|
2660
|
+
if (derived && derived !== message.address) {
|
|
2661
|
+
throw new Error(`Address "${message.address}" does not match its public key`);
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2558
2664
|
if (!(0, octez_connect_utils_1.isValidAddress)(address)) {
|
|
2559
2665
|
throw new Error(`Invalid address: "${address}"`);
|
|
2560
2666
|
}
|