@tezos-x/octez.connect-dapp 4.8.4 → 4.8.6
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/README.md +41 -0
- package/dist/cjs/dapp-client/DAppClient.d.ts +113 -4
- package/dist/cjs/dapp-client/DAppClient.js +962 -373
- package/dist/cjs/dapp-client/DAppClient.js.map +1 -1
- package/dist/cjs/dapp-client/DAppClientOptions.d.ts +17 -0
- package/dist/cjs/dapp-client/DAppClientOptions.js.map +1 -1
- package/dist/cjs/events.d.ts +27 -2
- package/dist/cjs/events.js +9 -5
- package/dist/cjs/events.js.map +1 -1
- package/dist/cjs/index.d.ts +4 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/utils/get-instance.js +3 -0
- package/dist/cjs/utils/get-instance.js.map +1 -1
- package/dist/esm/dapp-client/DAppClient.d.ts +113 -4
- package/dist/esm/dapp-client/DAppClient.js +968 -368
- package/dist/esm/dapp-client/DAppClient.js.map +1 -1
- package/dist/esm/dapp-client/DAppClientOptions.d.ts +17 -0
- package/dist/esm/dapp-client/DAppClientOptions.js.map +1 -1
- package/dist/esm/events.d.ts +27 -2
- package/dist/esm/events.js +6 -3
- package/dist/esm/events.js.map +1 -1
- package/dist/esm/index.d.ts +4 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/utils/get-instance.js +3 -0
- package/dist/esm/utils/get-instance.js.map +1 -1
- package/dist/octez.connect.dapp.min.js +1 -1
- package/package.json +9 -9
|
@@ -24,6 +24,23 @@ const octez_connect_transport_postmessage_1 = require("@tezos-x/octez.connect-tr
|
|
|
24
24
|
const octez_connect_ui_1 = require("@tezos-x/octez.connect-ui");
|
|
25
25
|
const octez_connect_transport_walletconnect_1 = require("@tezos-x/octez.connect-transport-walletconnect");
|
|
26
26
|
const logger = new octez_connect_core_1.Logger('DAppClient');
|
|
27
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 60000;
|
|
28
|
+
const SESSION_UPDATE_REQUEST_ID = 'session_update';
|
|
29
|
+
const createLazyPromise = (factory) => {
|
|
30
|
+
let promise;
|
|
31
|
+
const getPromise = () => {
|
|
32
|
+
if (!promise) {
|
|
33
|
+
promise = factory();
|
|
34
|
+
}
|
|
35
|
+
return promise;
|
|
36
|
+
};
|
|
37
|
+
const lazyPromise = {
|
|
38
|
+
then: (onfulfilled, onrejected) => getPromise().then(onfulfilled, onrejected),
|
|
39
|
+
catch: (onrejected) => getPromise().catch(onrejected),
|
|
40
|
+
finally: (onfinally) => getPromise().finally(onfinally !== null && onfinally !== void 0 ? onfinally : undefined)
|
|
41
|
+
};
|
|
42
|
+
return lazyPromise;
|
|
43
|
+
};
|
|
27
44
|
/**
|
|
28
45
|
* @publicapi
|
|
29
46
|
*
|
|
@@ -34,7 +51,7 @@ const logger = new octez_connect_core_1.Logger('DAppClient');
|
|
|
34
51
|
*/
|
|
35
52
|
class DAppClient extends octez_connect_core_1.Client {
|
|
36
53
|
constructor(config) {
|
|
37
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
54
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
38
55
|
super(Object.assign({ storage: config && config.storage ? config.storage : new octez_connect_core_1.LocalStorage() }, config));
|
|
39
56
|
this.events = new events_1.BeaconEventHandler();
|
|
40
57
|
this.isGetActiveAccountHandled = false;
|
|
@@ -43,6 +60,8 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
43
60
|
* A map of requests that are currently "open", meaning we have sent them to a wallet and are still awaiting a response.
|
|
44
61
|
*/
|
|
45
62
|
this.openRequests = new Map();
|
|
63
|
+
this.acknowledgedRequests = new Set();
|
|
64
|
+
this.openRequestTimeouts = new Map();
|
|
46
65
|
/**
|
|
47
66
|
* The currently active account. For all requests that are associated to a specific request (operation request, signing request),
|
|
48
67
|
* the active account is used to determine the network and destination wallet
|
|
@@ -52,44 +71,59 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
52
71
|
* The currently active peer. This is used to address a peer in case the active account is not set. (Eg. for permission requests)
|
|
53
72
|
*/
|
|
54
73
|
this._activePeer = new octez_connect_utils_1.ExposedPromise();
|
|
55
|
-
this.
|
|
74
|
+
this.destroyed = false;
|
|
56
75
|
this.beaconIDB = new octez_connect_core_1.IndexedDBStorage('beacon', ['bug_report', 'metrics']);
|
|
57
76
|
this.debounceSetActiveAccount = false;
|
|
77
|
+
// Constructor-time invalid storage recovery can race through multiple async paths.
|
|
78
|
+
// Keep this guard scoped to those startup paths.
|
|
79
|
+
this.hasEmittedInvalidAccountDeactivated = false;
|
|
80
|
+
this._disconnectNotifyPeers = false;
|
|
58
81
|
this.multiTabChannel = new octez_connect_core_1.MultiTabChannel('octez.connect-sdk-channel', this.onBCMessageHandler.bind(this), this.onElectedLeaderhandler.bind(this));
|
|
59
82
|
this.blockchains = new Map();
|
|
60
83
|
this.description = config.description;
|
|
61
|
-
this.
|
|
62
|
-
|
|
84
|
+
this.disableWalletConnect = (_a = config.disableWalletConnect) !== null && _a !== void 0 ? _a : false;
|
|
85
|
+
if (!this.disableWalletConnect) {
|
|
86
|
+
this.wcProjectId = ((_b = config.walletConnectOptions) === null || _b === void 0 ? void 0 : _b.projectId) || '24469fd0a06df227b6e5f7dc7de0ff4f';
|
|
87
|
+
this.wcRelayUrl = (_c = config.walletConnectOptions) === null || _c === void 0 ? void 0 : _c.relayUrl;
|
|
88
|
+
}
|
|
63
89
|
this.featuredWallets = config.featuredWallets;
|
|
64
|
-
this.events = new events_1.BeaconEventHandler(config.eventHandlers, (
|
|
65
|
-
this.blockExplorer = (
|
|
66
|
-
this.network = (
|
|
67
|
-
(0, octez_connect_ui_1.setColorMode)((
|
|
90
|
+
this.events = new events_1.BeaconEventHandler(config.eventHandlers, (_d = config.disableDefaultEvents) !== null && _d !== void 0 ? _d : false);
|
|
91
|
+
this.blockExplorer = (_e = config.blockExplorer) !== null && _e !== void 0 ? _e : new tzkt_blockexplorer_1.TzktBlockExplorer();
|
|
92
|
+
this.network = (_f = config.network) !== null && _f !== void 0 ? _f : { type: (_g = config.preferredNetwork) !== null && _g !== void 0 ? _g : octez_connect_types_1.NetworkType.MAINNET };
|
|
93
|
+
(0, octez_connect_ui_1.setColorMode)((_h = config.colorMode) !== null && _h !== void 0 ? _h : octez_connect_types_1.ColorMode.LIGHT);
|
|
68
94
|
this.disclaimerText = config.disclaimerText;
|
|
69
|
-
this.errorMessages = (
|
|
95
|
+
this.errorMessages = (_j = config.errorMessages) !== null && _j !== void 0 ? _j : {};
|
|
70
96
|
this.appMetadataManager = new octez_connect_core_1.AppMetadataManager(this.storage);
|
|
71
97
|
this.storageValidator = new octez_connect_core_1.StorageValidator(this.storage);
|
|
72
98
|
this.enableAppSwitching =
|
|
73
99
|
config.enableAppSwitching === undefined ? true : !!config.enableAppSwitching;
|
|
74
100
|
this.enableMetrics = config.enableMetrics ? true : false;
|
|
101
|
+
this.requestTimeoutMs = (_k = config.requestTimeoutMs) !== null && _k !== void 0 ? _k : DEFAULT_REQUEST_TIMEOUT_MS;
|
|
75
102
|
// Subscribe to storage changes and update the active account if it changes on other tabs
|
|
76
103
|
this.storage.subscribeToStorageChanged((event) => __awaiter(this, void 0, void 0, function* () {
|
|
104
|
+
if (event.oldValue === event.newValue) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
77
107
|
if (event.eventType === 'storageCleared') {
|
|
78
|
-
this.setActiveAccount(undefined);
|
|
108
|
+
yield this.setActiveAccount(undefined);
|
|
79
109
|
return;
|
|
80
110
|
}
|
|
81
111
|
if (event.eventType === 'entryModified') {
|
|
82
112
|
if (event.key === this.storage.getPrefixedKey(octez_connect_types_1.StorageKey.ACTIVE_ACCOUNT)) {
|
|
83
113
|
const accountIdentifier = event.newValue;
|
|
84
114
|
if (!accountIdentifier || accountIdentifier === 'undefined') {
|
|
85
|
-
this.setActiveAccount(undefined);
|
|
115
|
+
yield this.setActiveAccount(undefined);
|
|
86
116
|
}
|
|
87
117
|
else {
|
|
88
118
|
const account = yield this.getAccount(accountIdentifier);
|
|
89
|
-
this.setActiveAccount(account);
|
|
119
|
+
yield this.setActiveAccount(account);
|
|
90
120
|
}
|
|
91
121
|
return;
|
|
92
122
|
}
|
|
123
|
+
if (event.key === this.storage.getPrefixedKey(octez_connect_types_1.StorageKey.ACCOUNTS)) {
|
|
124
|
+
yield this.recoverActiveAccountFromAccountsChange();
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
93
127
|
if (event.key === this.storage.getPrefixedKey(octez_connect_types_1.StorageKey.ENABLE_METRICS)) {
|
|
94
128
|
this.enableMetrics = !!(yield this.storage.get(octez_connect_types_1.StorageKey.ENABLE_METRICS));
|
|
95
129
|
return;
|
|
@@ -105,31 +139,43 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
105
139
|
this.activeAccountLoaded = this.storage
|
|
106
140
|
.get(octez_connect_types_1.StorageKey.ACTIVE_ACCOUNT)
|
|
107
141
|
.then((activeAccountIdentifier) => __awaiter(this, void 0, void 0, function* () {
|
|
142
|
+
const accounts = yield this.accountManager.getAccounts();
|
|
143
|
+
if (activeAccountIdentifier === 'undefined') {
|
|
144
|
+
// Non-normalizing storage backends can still surface legacy sentinel strings directly.
|
|
145
|
+
yield this.deactivateInvalidAccountState('missing_active_account');
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
108
148
|
if (activeAccountIdentifier) {
|
|
109
|
-
const account =
|
|
149
|
+
const account = accounts.find((storedAccount) => storedAccount.accountIdentifier === activeAccountIdentifier);
|
|
150
|
+
if (!account) {
|
|
151
|
+
yield this.deactivateInvalidAccountState('missing_active_account');
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
110
154
|
yield this.setActiveAccount(account);
|
|
111
155
|
return account;
|
|
112
156
|
}
|
|
113
|
-
|
|
114
|
-
yield this.
|
|
157
|
+
if (accounts.length > 0 && this.hasStoredActiveAccountPointer()) {
|
|
158
|
+
yield this.deactivateInvalidAccountState('missing_active_account');
|
|
115
159
|
return undefined;
|
|
116
160
|
}
|
|
161
|
+
yield this.setActiveAccount(undefined);
|
|
162
|
+
return undefined;
|
|
117
163
|
}))
|
|
118
164
|
.catch((storageError) => __awaiter(this, void 0, void 0, function* () {
|
|
119
165
|
logger.error(storageError);
|
|
120
|
-
yield this.
|
|
121
|
-
this.events.emit(events_1.BeaconEvent.INVALID_ACCOUNT_DEACTIVATED);
|
|
166
|
+
yield this.deactivateInvalidAccountState('invalid_active_account_storage');
|
|
122
167
|
return undefined;
|
|
123
168
|
}));
|
|
124
169
|
this.handleResponse = (message, connectionInfo) => __awaiter(this, void 0, void 0, function* () {
|
|
125
170
|
var _a;
|
|
126
|
-
const
|
|
171
|
+
const isV3WrappedMessage = message.version === '3' && 'message' in message;
|
|
172
|
+
const typedMessage = isV3WrappedMessage
|
|
127
173
|
? message.message
|
|
128
174
|
: message;
|
|
129
|
-
let appMetadata =
|
|
175
|
+
let appMetadata = isV3WrappedMessage
|
|
130
176
|
? (_a = typedMessage.blockchainData) === null || _a === void 0 ? void 0 : _a.appMetadata
|
|
131
177
|
: typedMessage.appMetadata;
|
|
132
|
-
if (!appMetadata &&
|
|
178
|
+
if (!appMetadata && isV3WrappedMessage) {
|
|
133
179
|
const storedMetadata = yield Promise.all([
|
|
134
180
|
this.storage.get(octez_connect_types_1.StorageKey.TRANSPORT_P2P_PEERS_DAPP),
|
|
135
181
|
this.storage.get(octez_connect_types_1.StorageKey.TRANSPORT_WALLETCONNECT_PEERS_DAPP),
|
|
@@ -158,8 +204,11 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
158
204
|
},
|
|
159
205
|
id: message.id
|
|
160
206
|
});
|
|
161
|
-
if (typedMessage.type
|
|
162
|
-
this.
|
|
207
|
+
if (typedMessage.type === octez_connect_types_1.BeaconMessageType.Acknowledge) {
|
|
208
|
+
this.clearOpenRequestTimeout(message.id);
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
this.clearOpenRequest(message.id);
|
|
163
212
|
}
|
|
164
213
|
return;
|
|
165
214
|
}
|
|
@@ -187,15 +236,19 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
187
236
|
yield this.events.emit(events_1.BeaconEvent.CHANNEL_CLOSED);
|
|
188
237
|
});
|
|
189
238
|
if (openRequest && typedMessage.type === octez_connect_types_1.BeaconMessageType.Acknowledge) {
|
|
190
|
-
this.
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
.
|
|
194
|
-
message
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
239
|
+
if (!this.acknowledgedRequests.has(message.id)) {
|
|
240
|
+
this.acknowledgedRequests.add(message.id);
|
|
241
|
+
this.clearOpenRequestTimeout(message.id);
|
|
242
|
+
this.analytics.track('event', 'DAppClient', 'Acknowledge received from Wallet');
|
|
243
|
+
logger.log('handleResponse', `acknowledge message received for ${message.id}`);
|
|
244
|
+
this.events
|
|
245
|
+
.emit(events_1.BeaconEvent.ACKNOWLEDGE_RECEIVED, {
|
|
246
|
+
message: typedMessage,
|
|
247
|
+
extraInfo: {},
|
|
248
|
+
walletInfo: yield this.getWalletInfo()
|
|
249
|
+
})
|
|
250
|
+
.catch((emitError) => logger.error('handleResponse', emitError));
|
|
251
|
+
}
|
|
199
252
|
}
|
|
200
253
|
else if (openRequest) {
|
|
201
254
|
if (typedMessage.type === octez_connect_types_1.BeaconMessageType.PermissionResponse && appMetadata) {
|
|
@@ -207,7 +260,8 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
207
260
|
else {
|
|
208
261
|
openRequest.resolve({ message, connectionInfo });
|
|
209
262
|
}
|
|
210
|
-
this.
|
|
263
|
+
this.clearOpenRequest(message.id);
|
|
264
|
+
this.acknowledgedRequests.delete(message.id);
|
|
211
265
|
}
|
|
212
266
|
else {
|
|
213
267
|
if (typedMessage.type === octez_connect_types_1.BeaconMessageType.Disconnect) {
|
|
@@ -216,16 +270,23 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
216
270
|
else if (typedMessage.type === octez_connect_types_1.BeaconMessageType.ChangeAccountRequest) {
|
|
217
271
|
yield this.onNewAccount(typedMessage, connectionInfo);
|
|
218
272
|
}
|
|
273
|
+
else {
|
|
274
|
+
logger.warn('handleResponse', 'received response for unknown or closed request', {
|
|
275
|
+
id: message.id,
|
|
276
|
+
type: typedMessage.type,
|
|
277
|
+
connectionInfo
|
|
278
|
+
});
|
|
279
|
+
}
|
|
219
280
|
}
|
|
220
281
|
if (this._transport.isResolved()) {
|
|
221
282
|
const transport = yield this.transport;
|
|
222
283
|
if (transport instanceof octez_connect_transport_walletconnect_1.WalletConnectTransport &&
|
|
223
|
-
!this.openRequests.has(
|
|
224
|
-
this.openRequests.set(
|
|
284
|
+
!this.openRequests.has(SESSION_UPDATE_REQUEST_ID)) {
|
|
285
|
+
this.openRequests.set(SESSION_UPDATE_REQUEST_ID, new octez_connect_utils_1.ExposedPromise());
|
|
225
286
|
}
|
|
226
287
|
}
|
|
227
288
|
});
|
|
228
|
-
this.storageValidator
|
|
289
|
+
this.storageValidated = this.storageValidator
|
|
229
290
|
.validate()
|
|
230
291
|
.then((isValid) => __awaiter(this, void 0, void 0, function* () {
|
|
231
292
|
var _a, _b;
|
|
@@ -242,15 +303,22 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
242
303
|
url: info.deeplink
|
|
243
304
|
});
|
|
244
305
|
const nowValid = yield this.storageValidator.validate();
|
|
245
|
-
if (!nowValid) {
|
|
246
|
-
this.
|
|
306
|
+
if (!nowValid && account) {
|
|
307
|
+
yield this.deactivateInvalidAccountState('storage_validation_failed');
|
|
308
|
+
}
|
|
309
|
+
else if (!nowValid) {
|
|
310
|
+
yield this.resetInvalidState(false);
|
|
247
311
|
}
|
|
248
312
|
}
|
|
249
313
|
if (account && account.origin.type !== 'p2p') {
|
|
250
|
-
|
|
314
|
+
// Fire-and-forget eager init for stored sessions. init() can now
|
|
315
|
+
// reject (e.g. when an abort path settles _initPromise), so swallow
|
|
316
|
+
// here; downstream callers of init() handle rejection on their own.
|
|
317
|
+
this.init().catch((initError) => { var _a; return logger.error('eager init failed', (_a = initError === null || initError === void 0 ? void 0 : initError.message) !== null && _a !== void 0 ? _a : initError); });
|
|
251
318
|
}
|
|
252
319
|
}))
|
|
253
320
|
.catch((err) => logger.error(err.message));
|
|
321
|
+
this.retainStorageValidationPromise();
|
|
254
322
|
this.sendMetrics('enable-metrics?' + this.addQueryParam('version', octez_connect_core_1.SDK_VERSION), undefined, (res) => {
|
|
255
323
|
if (!res.ok) {
|
|
256
324
|
res.status === 426
|
|
@@ -328,11 +396,42 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
328
396
|
const transport = (yield this._transport.promise);
|
|
329
397
|
yield transport.waitForResolution();
|
|
330
398
|
this.openRequestsOtherTabs.add(message.id);
|
|
331
|
-
isV3
|
|
399
|
+
const requestPromise = isV3
|
|
332
400
|
? this.makeRequestV3(message.data, message.id)
|
|
333
401
|
: this.makeRequest(message.data, false, message.id);
|
|
402
|
+
requestPromise.catch((requestError) => {
|
|
403
|
+
const errorResponse = this.buildDelegatedRequestError(message.id, requestError);
|
|
404
|
+
this.multiTabChannel.postMessage({
|
|
405
|
+
type: 'RESPONSE',
|
|
406
|
+
data: {
|
|
407
|
+
message: errorResponse,
|
|
408
|
+
connectionInfo: {
|
|
409
|
+
origin: octez_connect_types_1.Origin.WALLETCONNECT,
|
|
410
|
+
id: ''
|
|
411
|
+
}
|
|
412
|
+
},
|
|
413
|
+
id: message.id
|
|
414
|
+
});
|
|
415
|
+
this.clearOpenRequest(message.id);
|
|
416
|
+
});
|
|
334
417
|
});
|
|
335
418
|
}
|
|
419
|
+
buildDelegatedRequestError(id, error) {
|
|
420
|
+
if (typeof error === 'object' &&
|
|
421
|
+
error !== null &&
|
|
422
|
+
'type' in error &&
|
|
423
|
+
'errorType' in error &&
|
|
424
|
+
error.type === octez_connect_types_1.BeaconMessageType.Error) {
|
|
425
|
+
return Object.assign(Object.assign({}, error), { id });
|
|
426
|
+
}
|
|
427
|
+
return {
|
|
428
|
+
id,
|
|
429
|
+
type: octez_connect_types_1.BeaconMessageType.Error,
|
|
430
|
+
errorType: octez_connect_types_1.BeaconErrorType.UNKNOWN_ERROR,
|
|
431
|
+
senderId: '',
|
|
432
|
+
version: '2'
|
|
433
|
+
};
|
|
434
|
+
}
|
|
336
435
|
createStateSnapshot() {
|
|
337
436
|
return __awaiter(this, void 0, void 0, function* () {
|
|
338
437
|
if (!localStorage || !this.enableMetrics) {
|
|
@@ -363,10 +462,7 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
363
462
|
initInternalTransports() {
|
|
364
463
|
return __awaiter(this, void 0, void 0, function* () {
|
|
365
464
|
var _a, _b;
|
|
366
|
-
const seed = yield this.
|
|
367
|
-
if (!seed) {
|
|
368
|
-
throw new Error('Secret seed not found');
|
|
369
|
-
}
|
|
465
|
+
const seed = yield this.getOrCreateSDKSecretSeed();
|
|
370
466
|
const keyPair = yield (0, octez_connect_utils_1.getKeypairFromSeed)(seed);
|
|
371
467
|
if (this.postMessageTransport || this.p2pTransport || this.walletConnectTransport) {
|
|
372
468
|
return;
|
|
@@ -375,6 +471,11 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
375
471
|
yield this.addListener(this.postMessageTransport);
|
|
376
472
|
this.p2pTransport = new DappP2PTransport_1.DappP2PTransport(this.name, keyPair, this.storage, this.matrixNodes, this.iconUrl, this.appUrl);
|
|
377
473
|
yield this.addListener(this.p2pTransport);
|
|
474
|
+
// Skip WalletConnect entirely when disabled (e.g. inside a Firefox MV3 content-script
|
|
475
|
+
// compartment where the WC provider cannot run). postMessage + P2P are unaffected.
|
|
476
|
+
if (this.disableWalletConnect) {
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
378
479
|
const wcOptions = {
|
|
379
480
|
projectId: this.wcProjectId,
|
|
380
481
|
relayUrl: this.wcRelayUrl,
|
|
@@ -393,6 +494,41 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
393
494
|
yield this.addListener(this.walletConnectTransport);
|
|
394
495
|
});
|
|
395
496
|
}
|
|
497
|
+
getOrCreateSDKSecretSeed() {
|
|
498
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
499
|
+
if (this._sdkSecretSeedPromise) {
|
|
500
|
+
return this._sdkSecretSeedPromise;
|
|
501
|
+
}
|
|
502
|
+
this._sdkSecretSeedPromise = this.loadOrCreateSDKSecretSeed()
|
|
503
|
+
.finally(() => {
|
|
504
|
+
this._sdkSecretSeedPromise = undefined;
|
|
505
|
+
})
|
|
506
|
+
.catch((error) => {
|
|
507
|
+
throw error;
|
|
508
|
+
});
|
|
509
|
+
return this._sdkSecretSeedPromise;
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
loadOrCreateSDKSecretSeed() {
|
|
513
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
514
|
+
const existingSeed = yield this.storage.get(octez_connect_types_1.StorageKey.BEACON_SDK_SECRET_SEED);
|
|
515
|
+
if (this.isValidSDKSecretSeed(existingSeed)) {
|
|
516
|
+
return existingSeed;
|
|
517
|
+
}
|
|
518
|
+
yield this.storage.delete(octez_connect_types_1.StorageKey.BEACON_SDK_SECRET_SEED);
|
|
519
|
+
this._keyPair = new octez_connect_utils_1.ExposedPromise();
|
|
520
|
+
this._beaconId = new octez_connect_utils_1.ExposedPromise();
|
|
521
|
+
yield this.initSDK();
|
|
522
|
+
const restoredSeed = yield this.storage.get(octez_connect_types_1.StorageKey.BEACON_SDK_SECRET_SEED);
|
|
523
|
+
if (this.isValidSDKSecretSeed(restoredSeed)) {
|
|
524
|
+
return restoredSeed;
|
|
525
|
+
}
|
|
526
|
+
throw new Error('Secret seed not found');
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
isValidSDKSecretSeed(seed) {
|
|
530
|
+
return typeof seed === 'string' && seed !== '' && seed !== 'undefined' && seed !== 'null';
|
|
531
|
+
}
|
|
396
532
|
initEvents() {
|
|
397
533
|
if (!this.walletConnectTransport) {
|
|
398
534
|
return;
|
|
@@ -410,6 +546,7 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
410
546
|
}
|
|
411
547
|
wcToastHandler(status) {
|
|
412
548
|
return __awaiter(this, void 0, void 0, function* () {
|
|
549
|
+
var _a;
|
|
413
550
|
const walletInfo = yield (() => __awaiter(this, void 0, void 0, function* () {
|
|
414
551
|
try {
|
|
415
552
|
return yield this.getWalletInfo();
|
|
@@ -427,22 +564,28 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
427
564
|
});
|
|
428
565
|
}
|
|
429
566
|
else {
|
|
430
|
-
this
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
567
|
+
// The WC transport reports session-proposal rejection through this event
|
|
568
|
+
// when there are no active listeners yet (no peer paired). Reject the
|
|
569
|
+
// in-flight init promise so `await dapp.requestPermissions()` unwinds
|
|
570
|
+
// through requestPermissions' catch -> handleRequestError, which both
|
|
571
|
+
// emits PERMISSION_REQUEST_ERROR and resets transport state. Without
|
|
572
|
+
// this, the dapp wedges with a pending `_initPromise`.
|
|
573
|
+
(_a = this._initReject) === null || _a === void 0 ? void 0 : _a.call(this, this.createAbortedError());
|
|
434
574
|
}
|
|
435
575
|
});
|
|
436
576
|
}
|
|
437
577
|
channelClosedHandler(type) {
|
|
438
578
|
return __awaiter(this, void 0, void 0, function* () {
|
|
579
|
+
if (!this._transport.isResolved()) {
|
|
580
|
+
yield this.events.emit(events_1.BeaconEvent.CHANNEL_CLOSED);
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
439
583
|
const transport = yield this.transport;
|
|
440
584
|
if (transport.type !== type) {
|
|
441
585
|
return;
|
|
442
586
|
}
|
|
443
587
|
yield this.events.emit(events_1.BeaconEvent.CHANNEL_CLOSED);
|
|
444
|
-
this.
|
|
445
|
-
yield this.disconnect();
|
|
588
|
+
yield this.disconnect({ notifyPeers: false });
|
|
446
589
|
});
|
|
447
590
|
}
|
|
448
591
|
/**
|
|
@@ -457,16 +600,81 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
457
600
|
destroy: { get: () => super.destroy }
|
|
458
601
|
});
|
|
459
602
|
return __awaiter(this, void 0, void 0, function* () {
|
|
603
|
+
this.destroyed = true;
|
|
460
604
|
yield this.createStateSnapshot();
|
|
605
|
+
yield this.destroyInternalTransports();
|
|
461
606
|
yield _super.destroy.call(this);
|
|
462
607
|
});
|
|
463
608
|
}
|
|
609
|
+
destroyInternalTransports() {
|
|
610
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
611
|
+
this.abortPendingInit();
|
|
612
|
+
const transports = [];
|
|
613
|
+
if (this.postMessageTransport) {
|
|
614
|
+
transports.push(this.postMessageTransport);
|
|
615
|
+
}
|
|
616
|
+
if (this.p2pTransport) {
|
|
617
|
+
transports.push(this.p2pTransport);
|
|
618
|
+
}
|
|
619
|
+
if (this.walletConnectTransport) {
|
|
620
|
+
transports.push(this.walletConnectTransport);
|
|
621
|
+
}
|
|
622
|
+
if (transports.length > 0) {
|
|
623
|
+
yield this.cleanup(transports);
|
|
624
|
+
yield Promise.all(transports.map((transport) => __awaiter(this, void 0, void 0, function* () {
|
|
625
|
+
yield transport.disconnect();
|
|
626
|
+
if ('doClientCleanup' in transport) {
|
|
627
|
+
yield transport.doClientCleanup();
|
|
628
|
+
}
|
|
629
|
+
})));
|
|
630
|
+
this.postMessageTransport = this.p2pTransport = this.walletConnectTransport = undefined;
|
|
631
|
+
yield this.setTransport(undefined);
|
|
632
|
+
}
|
|
633
|
+
yield this.multiTabChannel.close();
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
abortPendingInit() {
|
|
637
|
+
var _a;
|
|
638
|
+
(_a = this._initReject) === null || _a === void 0 ? void 0 : _a.call(this, this.createAbortedError());
|
|
639
|
+
this._initPromise = undefined;
|
|
640
|
+
this._initReject = undefined;
|
|
641
|
+
this._initSubstratePairing = undefined;
|
|
642
|
+
this._requestPermissionsPromise = undefined;
|
|
643
|
+
this._requestPermissionsKey = undefined;
|
|
644
|
+
}
|
|
645
|
+
createAbortedError(id = '') {
|
|
646
|
+
return {
|
|
647
|
+
type: octez_connect_types_1.BeaconMessageType.Error,
|
|
648
|
+
id,
|
|
649
|
+
senderId: '',
|
|
650
|
+
version: '2',
|
|
651
|
+
errorType: octez_connect_types_1.BeaconErrorType.ABORTED_ERROR
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* @internal
|
|
656
|
+
*/
|
|
657
|
+
isDestroyed() {
|
|
658
|
+
return this.destroyed;
|
|
659
|
+
}
|
|
660
|
+
assertNotDestroyed() {
|
|
661
|
+
if (this.destroyed) {
|
|
662
|
+
throw new Error('DAppClient has been destroyed and cannot be used again.');
|
|
663
|
+
}
|
|
664
|
+
}
|
|
464
665
|
init(transport, substratePairing) {
|
|
465
666
|
const _super = Object.create(null, {
|
|
466
667
|
init: { get: () => super.init }
|
|
467
668
|
});
|
|
468
669
|
return __awaiter(this, void 0, void 0, function* () {
|
|
670
|
+
this.assertNotDestroyed();
|
|
671
|
+
const requestedSubstratePairing = substratePairing === true;
|
|
469
672
|
if (this._initPromise) {
|
|
673
|
+
if (!transport &&
|
|
674
|
+
this._initReject &&
|
|
675
|
+
this._initSubstratePairing !== requestedSubstratePairing) {
|
|
676
|
+
throw new Error('Cannot start a permission request with different pairing options while another pairing is pending');
|
|
677
|
+
}
|
|
470
678
|
return this._initPromise;
|
|
471
679
|
}
|
|
472
680
|
try {
|
|
@@ -475,163 +683,260 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
475
683
|
catch (_a) {
|
|
476
684
|
//
|
|
477
685
|
}
|
|
478
|
-
this._initPromise = new Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
}
|
|
499
|
-
};
|
|
500
|
-
yield this.initInternalTransports();
|
|
501
|
-
if (!this.postMessageTransport || !this.p2pTransport || !this.walletConnectTransport) {
|
|
502
|
-
return;
|
|
686
|
+
this._initPromise = new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
687
|
+
this._initSubstratePairing = requestedSubstratePairing;
|
|
688
|
+
// Capture reject so abort paths (modal close, transport-level rejection)
|
|
689
|
+
// can unwedge a pending `await this.init()`. _initReject is cleared by
|
|
690
|
+
// the .finally below whether the promise resolves or rejects, so the
|
|
691
|
+
// field accurately means "currently rejectable" rather than "most recent
|
|
692
|
+
// rejector".
|
|
693
|
+
this._initReject = (reason) => {
|
|
694
|
+
this._initPromise = undefined;
|
|
695
|
+
reject(reason);
|
|
696
|
+
};
|
|
697
|
+
// The body uses `new Promise(async ...)` (an anti-pattern: unhandled
|
|
698
|
+
// throws are swallowed). Wrap it so any unexpected throw rejects the
|
|
699
|
+
// promise instead of stranding `await this.init()` callers forever.
|
|
700
|
+
// A proper structural fix would replace the async-executor entirely
|
|
701
|
+
// with chained promises; left as follow-up to keep this PR focused.
|
|
702
|
+
try {
|
|
703
|
+
if (transport) {
|
|
704
|
+
yield this.addListener(transport);
|
|
705
|
+
resolve(yield _super.init.call(this, transport));
|
|
503
706
|
}
|
|
504
|
-
this.
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
// Select the transport that matches the active account
|
|
508
|
-
if (origin === octez_connect_types_1.Origin.EXTENSION) {
|
|
509
|
-
resolve(yield _super.init.call(this, this.postMessageTransport));
|
|
510
|
-
}
|
|
511
|
-
else if (origin === octez_connect_types_1.Origin.P2P) {
|
|
512
|
-
resolve(yield _super.init.call(this, this.p2pTransport));
|
|
513
|
-
}
|
|
514
|
-
else if (origin === octez_connect_types_1.Origin.WALLETCONNECT) {
|
|
515
|
-
resolve(yield _super.init.call(this, this.walletConnectTransport));
|
|
516
|
-
}
|
|
707
|
+
else if (this._transport.isSettled()) {
|
|
708
|
+
yield (yield this.transport).connect();
|
|
709
|
+
resolve(yield _super.init.call(this, yield this.transport));
|
|
517
710
|
}
|
|
518
711
|
else {
|
|
519
|
-
const
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
this.
|
|
526
|
-
|
|
527
|
-
}
|
|
528
|
-
this.
|
|
529
|
-
.
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
.
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
712
|
+
const activeAccount = yield this.getActiveAccount();
|
|
713
|
+
const stopListening = () => {
|
|
714
|
+
const onError = (err) => logger.error('stopListeningForNewPeers', err);
|
|
715
|
+
if (this.postMessageTransport) {
|
|
716
|
+
this.postMessageTransport.stopListeningForNewPeers().catch(onError);
|
|
717
|
+
}
|
|
718
|
+
if (this.p2pTransport) {
|
|
719
|
+
this.p2pTransport.stopListeningForNewPeers().catch(onError);
|
|
720
|
+
}
|
|
721
|
+
if (this.walletConnectTransport) {
|
|
722
|
+
this.walletConnectTransport.stopListeningForNewPeers().catch(onError);
|
|
723
|
+
}
|
|
724
|
+
};
|
|
725
|
+
yield this.initInternalTransports();
|
|
726
|
+
if (!this.postMessageTransport ||
|
|
727
|
+
!this.p2pTransport ||
|
|
728
|
+
(!this.disableWalletConnect && !this.walletConnectTransport)) {
|
|
729
|
+
// Bare return here used to strand the promise forever -- no UI is
|
|
730
|
+
// emitted, no peer ever pairs, no abort path fires. Reject explicitly
|
|
731
|
+
// so callers see a deterministic error. (WalletConnect is exempt when
|
|
732
|
+
// disableWalletConnect is set -- its transport is intentionally absent.)
|
|
733
|
+
const noTransportError = {
|
|
734
|
+
type: octez_connect_types_1.BeaconMessageType.Error,
|
|
735
|
+
id: '',
|
|
736
|
+
senderId: '',
|
|
737
|
+
version: '2',
|
|
738
|
+
errorType: octez_connect_types_1.BeaconErrorType.UNKNOWN_ERROR
|
|
739
|
+
};
|
|
740
|
+
reject(noTransportError);
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
this.postMessageTransport.connect().then().catch(console.error);
|
|
744
|
+
if (activeAccount && activeAccount.origin) {
|
|
745
|
+
const origin = activeAccount.origin.type;
|
|
746
|
+
// Select the transport that matches the active account
|
|
747
|
+
if (origin === octez_connect_types_1.Origin.EXTENSION) {
|
|
748
|
+
resolve(yield _super.init.call(this, this.postMessageTransport));
|
|
749
|
+
}
|
|
750
|
+
else if (origin === octez_connect_types_1.Origin.P2P) {
|
|
751
|
+
resolve(yield _super.init.call(this, this.p2pTransport));
|
|
752
|
+
}
|
|
753
|
+
else if (origin === octez_connect_types_1.Origin.WALLETCONNECT && this.walletConnectTransport) {
|
|
754
|
+
resolve(yield _super.init.call(this, this.walletConnectTransport));
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
const p2pTransport = this.p2pTransport;
|
|
759
|
+
const postMessageTransport = this.postMessageTransport;
|
|
760
|
+
const walletConnectTransport = this.walletConnectTransport;
|
|
761
|
+
postMessageTransport
|
|
762
|
+
.listenForNewPeer((peer) => {
|
|
763
|
+
logger.log('init', 'postmessage transport peer connected', peer);
|
|
764
|
+
this.analytics.track('event', 'DAppClient', 'Extension connected', {
|
|
765
|
+
peerName: peer.name
|
|
766
|
+
});
|
|
767
|
+
this.events
|
|
768
|
+
.emit(events_1.BeaconEvent.PAIR_SUCCESS, peer)
|
|
769
|
+
.catch((emitError) => console.warn(emitError));
|
|
770
|
+
this.setActivePeer(peer).catch(console.error);
|
|
771
|
+
this.setTransport(this.postMessageTransport).catch(console.error);
|
|
772
|
+
stopListening();
|
|
773
|
+
resolve(octez_connect_types_1.TransportType.POST_MESSAGE);
|
|
774
|
+
})
|
|
775
|
+
.catch(console.error);
|
|
776
|
+
p2pTransport
|
|
777
|
+
.listenForNewPeer((peer) => {
|
|
778
|
+
logger.log('init', 'p2p transport peer connected', peer);
|
|
779
|
+
this.analytics.track('event', 'DAppClient', 'octez.connect Wallet connected', {
|
|
780
|
+
peerName: peer.name
|
|
781
|
+
});
|
|
782
|
+
this.events
|
|
783
|
+
.emit(events_1.BeaconEvent.PAIR_SUCCESS, peer)
|
|
784
|
+
.catch((emitError) => console.warn(emitError));
|
|
785
|
+
this.setActivePeer(peer).catch(console.error);
|
|
786
|
+
this.setTransport(this.p2pTransport).catch(console.error);
|
|
787
|
+
stopListening();
|
|
788
|
+
resolve(octez_connect_types_1.TransportType.P2P);
|
|
789
|
+
})
|
|
790
|
+
.catch(console.error);
|
|
791
|
+
walletConnectTransport === null || walletConnectTransport === void 0 ? void 0 : walletConnectTransport.listenForNewPeer((peer) => {
|
|
792
|
+
logger.log('init', 'walletconnect transport peer connected', peer);
|
|
793
|
+
this.analytics.track('event', 'DAppClient', 'WalletConnect Wallet connected', {
|
|
794
|
+
peerName: peer.name
|
|
795
|
+
});
|
|
796
|
+
this.events
|
|
797
|
+
.emit(events_1.BeaconEvent.PAIR_SUCCESS, peer)
|
|
798
|
+
.catch((emitError) => console.warn(emitError));
|
|
799
|
+
this.setActivePeer(peer).catch(console.error);
|
|
800
|
+
this.setTransport(this.walletConnectTransport).catch(console.error);
|
|
801
|
+
stopListening();
|
|
802
|
+
resolve(octez_connect_types_1.TransportType.WALLETCONNECT);
|
|
803
|
+
}).catch(console.error);
|
|
804
|
+
octez_connect_transport_postmessage_1.PostMessageTransport.getAvailableExtensions()
|
|
805
|
+
.then((extensions) => __awaiter(this, void 0, void 0, function* () {
|
|
806
|
+
this.analytics.track('event', 'DAppClient', 'Extensions detected', { extensions });
|
|
807
|
+
}))
|
|
808
|
+
.catch((error) => {
|
|
809
|
+
this._initPromise = undefined;
|
|
810
|
+
console.error(error);
|
|
542
811
|
});
|
|
543
|
-
this
|
|
544
|
-
|
|
545
|
-
.
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
812
|
+
const abortHandler = () => __awaiter(this, void 0, void 0, function* () {
|
|
813
|
+
var _a;
|
|
814
|
+
logger.log('init', 'ABORTED');
|
|
815
|
+
this.sendMetrics('performance-metrics/save', yield this.buildPayload('connect', 'abort'));
|
|
816
|
+
yield Promise.all([
|
|
817
|
+
postMessageTransport.disconnect(),
|
|
818
|
+
// p2pTransport.disconnect(), do not abort connection manually
|
|
819
|
+
walletConnectTransport === null || walletConnectTransport === void 0 ? void 0 : walletConnectTransport.disconnect()
|
|
820
|
+
]);
|
|
821
|
+
this.postMessageTransport = this.walletConnectTransport = this.p2pTransport = undefined;
|
|
822
|
+
this._activeAccount.isResolved() && this.clearActiveAccount();
|
|
823
|
+
// Reject _initPromise so any awaiter (makeRequest -> requestPermissions)
|
|
824
|
+
// unwinds via handleRequestError instead of hanging. _initReject
|
|
825
|
+
// also clears _initPromise as a side effect.
|
|
826
|
+
(_a = this._initReject) === null || _a === void 0 ? void 0 : _a.call(this, this.createAbortedError());
|
|
557
827
|
});
|
|
828
|
+
const serializer = new octez_connect_core_1.Serializer();
|
|
829
|
+
const p2pPeerInfo = new Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
|
|
830
|
+
try {
|
|
831
|
+
yield p2pTransport.connect();
|
|
832
|
+
}
|
|
833
|
+
catch (err) {
|
|
834
|
+
logger.error(err);
|
|
835
|
+
yield this.hideUI(['alert']); // hide pairing alert
|
|
836
|
+
setTimeout(() => this.events.emit(events_1.BeaconEvent.GENERIC_ERROR, err.message), 1000);
|
|
837
|
+
abortHandler().catch((abortErr) => logger.warn('init', 'abortHandler', abortErr));
|
|
838
|
+
resolve('');
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
resolve(yield serializer.serialize(yield p2pTransport.getPairingRequestInfo()));
|
|
842
|
+
}));
|
|
843
|
+
const walletConnectPeerInfo = walletConnectTransport
|
|
844
|
+
? createLazyPromise(() => walletConnectTransport
|
|
845
|
+
.getPairingRequestInfo()
|
|
846
|
+
.then((pairingRequestInfo) => pairingRequestInfo.uri)
|
|
847
|
+
.catch((error) => {
|
|
848
|
+
logger.warn('init', 'walletconnect pairing request failed', error);
|
|
849
|
+
return '';
|
|
850
|
+
}))
|
|
851
|
+
: undefined;
|
|
852
|
+
const postmessagePeerInfo = new Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
|
|
853
|
+
resolve(yield serializer.serialize(yield postMessageTransport.getPairingRequestInfo()));
|
|
854
|
+
}));
|
|
558
855
|
this.events
|
|
559
|
-
.emit(events_1.BeaconEvent.
|
|
856
|
+
.emit(events_1.BeaconEvent.PAIR_INIT, Object.assign(Object.assign({ p2pPeerInfo,
|
|
857
|
+
postmessagePeerInfo }, (walletConnectPeerInfo ? { walletConnectPeerInfo } : {})), { networkType: this.network.type, abortedHandler: abortHandler.bind(this), disclaimerText: this.disclaimerText, analytics: this.analytics, featuredWallets: this.featuredWallets, substratePairing }))
|
|
560
858
|
.catch((emitError) => console.warn(emitError));
|
|
561
|
-
|
|
562
|
-
this.setTransport(this.walletConnectTransport).catch(console.error);
|
|
563
|
-
stopListening();
|
|
564
|
-
resolve(octez_connect_types_1.TransportType.WALLETCONNECT);
|
|
565
|
-
})
|
|
566
|
-
.catch(console.error);
|
|
567
|
-
octez_connect_transport_postmessage_1.PostMessageTransport.getAvailableExtensions()
|
|
568
|
-
.then((extensions) => __awaiter(this, void 0, void 0, function* () {
|
|
569
|
-
this.analytics.track('event', 'DAppClient', 'Extensions detected', { extensions });
|
|
570
|
-
}))
|
|
571
|
-
.catch((error) => {
|
|
572
|
-
this._initPromise = undefined;
|
|
573
|
-
console.error(error);
|
|
574
|
-
});
|
|
575
|
-
const abortHandler = () => __awaiter(this, void 0, void 0, function* () {
|
|
576
|
-
logger.log('init', 'ABORTED');
|
|
577
|
-
this.sendMetrics('performance-metrics/save', yield this.buildPayload('connect', 'abort'));
|
|
578
|
-
yield Promise.all([
|
|
579
|
-
postMessageTransport.disconnect(),
|
|
580
|
-
// p2pTransport.disconnect(), do not abort connection manually
|
|
581
|
-
walletConnectTransport.disconnect()
|
|
582
|
-
]);
|
|
583
|
-
this.postMessageTransport = this.walletConnectTransport = this.p2pTransport = undefined;
|
|
584
|
-
this._activeAccount.isResolved() && this.clearActiveAccount();
|
|
585
|
-
this._initPromise = undefined;
|
|
586
|
-
});
|
|
587
|
-
const serializer = new octez_connect_core_1.Serializer();
|
|
588
|
-
const p2pPeerInfo = new Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
|
|
589
|
-
try {
|
|
590
|
-
yield p2pTransport.connect();
|
|
591
|
-
}
|
|
592
|
-
catch (err) {
|
|
593
|
-
logger.error(err);
|
|
594
|
-
yield this.hideUI(['alert']); // hide pairing alert
|
|
595
|
-
setTimeout(() => this.events.emit(events_1.BeaconEvent.GENERIC_ERROR, err.message), 1000);
|
|
596
|
-
abortHandler();
|
|
597
|
-
resolve('');
|
|
598
|
-
return;
|
|
599
|
-
}
|
|
600
|
-
resolve(yield serializer.serialize(yield p2pTransport.getPairingRequestInfo()));
|
|
601
|
-
}));
|
|
602
|
-
const walletConnectPeerInfo = new Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
|
|
603
|
-
resolve((yield walletConnectTransport.getPairingRequestInfo()).uri);
|
|
604
|
-
}));
|
|
605
|
-
const postmessagePeerInfo = new Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
|
|
606
|
-
resolve(yield serializer.serialize(yield postMessageTransport.getPairingRequestInfo()));
|
|
607
|
-
}));
|
|
608
|
-
this.events
|
|
609
|
-
.emit(events_1.BeaconEvent.PAIR_INIT, {
|
|
610
|
-
p2pPeerInfo,
|
|
611
|
-
postmessagePeerInfo,
|
|
612
|
-
walletConnectPeerInfo,
|
|
613
|
-
networkType: this.network.type,
|
|
614
|
-
abortedHandler: abortHandler.bind(this),
|
|
615
|
-
disclaimerText: this.disclaimerText,
|
|
616
|
-
analytics: this.analytics,
|
|
617
|
-
featuredWallets: this.featuredWallets,
|
|
618
|
-
substratePairing
|
|
619
|
-
})
|
|
620
|
-
.catch((emitError) => console.warn(emitError));
|
|
859
|
+
}
|
|
621
860
|
}
|
|
622
861
|
}
|
|
862
|
+
catch (err) {
|
|
863
|
+
// An async-executor throw would otherwise be silently swallowed,
|
|
864
|
+
// leaving _initPromise pending and every awaiter stranded.
|
|
865
|
+
reject(err);
|
|
866
|
+
}
|
|
623
867
|
}));
|
|
868
|
+
// Drop the _initReject reference whether init resolved or rejected, so the
|
|
869
|
+
// field is never stale. Using an explicit helper keeps the dangling
|
|
870
|
+
// observer chain out of statement position (which the lint rules forbid).
|
|
871
|
+
this.clearInitRejectOnSettle(this._initPromise);
|
|
624
872
|
return this._initPromise;
|
|
625
873
|
});
|
|
626
874
|
}
|
|
875
|
+
/**
|
|
876
|
+
* Attach a settle observer to {@link _initPromise} that drops
|
|
877
|
+
* {@link _initReject} once the promise settles (resolved or rejected).
|
|
878
|
+
* Errors on the observer chain are swallowed; only the original
|
|
879
|
+
* {@link _initPromise} surfaces them to its awaiters.
|
|
880
|
+
*/
|
|
881
|
+
clearInitRejectOnSettle(initPromise) {
|
|
882
|
+
initPromise
|
|
883
|
+
.finally(() => {
|
|
884
|
+
this._initReject = undefined;
|
|
885
|
+
this._initSubstratePairing = undefined;
|
|
886
|
+
})
|
|
887
|
+
.catch(() => {
|
|
888
|
+
// observer-only; original promise's rejection is delivered to awaiters
|
|
889
|
+
});
|
|
890
|
+
}
|
|
627
891
|
/**
|
|
628
892
|
* Returns the active account
|
|
629
893
|
*/
|
|
630
894
|
getActiveAccount() {
|
|
631
895
|
return __awaiter(this, void 0, void 0, function* () {
|
|
896
|
+
this.assertNotDestroyed();
|
|
632
897
|
return this._activeAccount.promise;
|
|
633
898
|
});
|
|
634
899
|
}
|
|
900
|
+
retainStorageValidationPromise() {
|
|
901
|
+
// Constructor validation is intentionally fire-and-forget, but retaining
|
|
902
|
+
// the promise gives tests a deterministic hook without changing public API.
|
|
903
|
+
this.storageValidated.catch((err) => logger.error(err.message));
|
|
904
|
+
}
|
|
905
|
+
deactivateInvalidAccountState(reason) {
|
|
906
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
907
|
+
if (this.hasEmittedInvalidAccountDeactivated) {
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
this.hasEmittedInvalidAccountDeactivated = true;
|
|
911
|
+
logger.log('deactivateInvalidAccountState', reason);
|
|
912
|
+
if (reason === 'missing_active_account') {
|
|
913
|
+
yield this.repairMissingActiveAccount();
|
|
914
|
+
}
|
|
915
|
+
else {
|
|
916
|
+
yield this.resetInvalidState(false);
|
|
917
|
+
}
|
|
918
|
+
yield this.events.emit(events_1.BeaconEvent.INVALID_ACCOUNT_DEACTIVATED, { reason });
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
repairMissingActiveAccount() {
|
|
922
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
923
|
+
this._activeAccount = octez_connect_utils_1.ExposedPromise.resolve(undefined);
|
|
924
|
+
yield this.storage.delete(octez_connect_types_1.StorageKey.ACTIVE_ACCOUNT);
|
|
925
|
+
yield this.setActivePeer(undefined);
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
hasStoredActiveAccountPointer() {
|
|
929
|
+
// LocalStorage-specific repair: that backend is where literal sentinel strings were produced.
|
|
930
|
+
if (typeof localStorage === 'undefined') {
|
|
931
|
+
return false;
|
|
932
|
+
}
|
|
933
|
+
try {
|
|
934
|
+
return localStorage.getItem(this.storage.getPrefixedKey(octez_connect_types_1.StorageKey.ACTIVE_ACCOUNT)) !== null;
|
|
935
|
+
}
|
|
936
|
+
catch (_a) {
|
|
937
|
+
return false;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
635
940
|
isInvalidState(account) {
|
|
636
941
|
return __awaiter(this, void 0, void 0, function* () {
|
|
637
942
|
const activeAccount = yield this._activeAccount.promise;
|
|
@@ -643,11 +948,15 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
643
948
|
resetInvalidState() {
|
|
644
949
|
return __awaiter(this, arguments, void 0, function* (emit = true) {
|
|
645
950
|
var _a, _b;
|
|
646
|
-
this.accountManager.removeAllAccounts();
|
|
951
|
+
yield this.accountManager.removeAllAccounts();
|
|
647
952
|
this._activeAccount = octez_connect_utils_1.ExposedPromise.resolve(undefined);
|
|
648
|
-
this.storage.
|
|
649
|
-
|
|
650
|
-
|
|
953
|
+
yield this.storage.delete(octez_connect_types_1.StorageKey.ACTIVE_ACCOUNT);
|
|
954
|
+
if (emit) {
|
|
955
|
+
yield this.events.emit(events_1.BeaconEvent.INVALID_ACTIVE_ACCOUNT_STATE);
|
|
956
|
+
}
|
|
957
|
+
else {
|
|
958
|
+
yield this.hideUI(['alert']);
|
|
959
|
+
}
|
|
651
960
|
yield Promise.all([
|
|
652
961
|
(_a = this.postMessageTransport) === null || _a === void 0 ? void 0 : _a.disconnect(),
|
|
653
962
|
(_b = this.walletConnectTransport) === null || _b === void 0 ? void 0 : _b.disconnect()
|
|
@@ -666,7 +975,8 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
666
975
|
setActiveAccount(account) {
|
|
667
976
|
return __awaiter(this, void 0, void 0, function* () {
|
|
668
977
|
var _a;
|
|
669
|
-
|
|
978
|
+
const activeAccountAlreadyCleared = !account && (yield this.isActiveAccountAlreadyCleared());
|
|
979
|
+
if (!activeAccountAlreadyCleared && !this.isGetActiveAccountHandled) {
|
|
670
980
|
console.warn(`An active account has been received, but no active subscription was found for BeaconEvent.ACTIVE_ACCOUNT_SET.`);
|
|
671
981
|
}
|
|
672
982
|
if (account && this._activeAccount.isSettled() && (yield this.isInvalidState(account))) {
|
|
@@ -678,9 +988,9 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
678
988
|
}
|
|
679
989
|
// when I'm resetting the activeAccount
|
|
680
990
|
if (!account && this._activeAccount.isResolved() && (yield this.getActiveAccount())) {
|
|
681
|
-
const transport = yield this.transport;
|
|
991
|
+
const transport = this._transport.isResolved() ? yield this.transport : undefined;
|
|
682
992
|
const activeAccount = yield this.getActiveAccount();
|
|
683
|
-
if (!
|
|
993
|
+
if (!activeAccount) {
|
|
684
994
|
return;
|
|
685
995
|
}
|
|
686
996
|
if (!this.debounceSetActiveAccount && transport instanceof octez_connect_transport_walletconnect_1.WalletConnectTransport) {
|
|
@@ -696,22 +1006,15 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
696
1006
|
type: 'DISCONNECT'
|
|
697
1007
|
});
|
|
698
1008
|
}
|
|
699
|
-
|
|
700
|
-
.filter(([id, _promise]) => id !== 'session_update')
|
|
701
|
-
.forEach(([id, promise]) => {
|
|
702
|
-
promise.reject({
|
|
703
|
-
type: octez_connect_types_1.BeaconMessageType.Error,
|
|
704
|
-
errorType: octez_connect_types_1.BeaconErrorType.ABORTED_ERROR,
|
|
705
|
-
id,
|
|
706
|
-
senderId: '',
|
|
707
|
-
version: '2'
|
|
708
|
-
});
|
|
709
|
-
});
|
|
710
|
-
this.openRequests.clear();
|
|
1009
|
+
this.abortOpenRequests();
|
|
711
1010
|
this.debounceSetActiveAccount = false;
|
|
712
1011
|
}
|
|
713
1012
|
}
|
|
714
|
-
if (
|
|
1013
|
+
if (activeAccountAlreadyCleared) {
|
|
1014
|
+
// Keep peer and transport cleanup below idempotent, but do not write storage or emit
|
|
1015
|
+
// another ACTIVE_ACCOUNT_SET(undefined) for an already-empty account.
|
|
1016
|
+
}
|
|
1017
|
+
else if (this._activeAccount.isSettled()) {
|
|
715
1018
|
// If the promise has already been resolved we need to create a new one.
|
|
716
1019
|
this._activeAccount = octez_connect_utils_1.ExposedPromise.resolve(account);
|
|
717
1020
|
}
|
|
@@ -752,11 +1055,42 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
752
1055
|
yield this.setActivePeer(undefined);
|
|
753
1056
|
yield this.setTransport(undefined);
|
|
754
1057
|
}
|
|
755
|
-
|
|
756
|
-
|
|
1058
|
+
if (account) {
|
|
1059
|
+
yield this.storage.set(octez_connect_types_1.StorageKey.ACTIVE_ACCOUNT, account.accountIdentifier);
|
|
1060
|
+
}
|
|
1061
|
+
else if (!activeAccountAlreadyCleared) {
|
|
1062
|
+
yield this.storage.delete(octez_connect_types_1.StorageKey.ACTIVE_ACCOUNT);
|
|
1063
|
+
}
|
|
1064
|
+
if (!activeAccountAlreadyCleared) {
|
|
1065
|
+
yield this.events.emit(events_1.BeaconEvent.ACTIVE_ACCOUNT_SET, account);
|
|
1066
|
+
}
|
|
757
1067
|
return;
|
|
758
1068
|
});
|
|
759
1069
|
}
|
|
1070
|
+
isActiveAccountAlreadyCleared() {
|
|
1071
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1072
|
+
return this._activeAccount.isResolved() && !(yield this.getActiveAccount());
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
recoverActiveAccountFromAccountsChange() {
|
|
1076
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1077
|
+
const activeAccountIdentifier = yield this.storage.get(octez_connect_types_1.StorageKey.ACTIVE_ACCOUNT);
|
|
1078
|
+
if (!activeAccountIdentifier || activeAccountIdentifier === 'undefined') {
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
if (this._activeAccount.isResolved()) {
|
|
1082
|
+
const activeAccount = yield this.getActiveAccount();
|
|
1083
|
+
if ((activeAccount === null || activeAccount === void 0 ? void 0 : activeAccount.accountIdentifier) === activeAccountIdentifier) {
|
|
1084
|
+
return;
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
const account = yield this.getAccount(activeAccountIdentifier);
|
|
1088
|
+
if (!account) {
|
|
1089
|
+
return;
|
|
1090
|
+
}
|
|
1091
|
+
yield this.setActiveAccount(account);
|
|
1092
|
+
});
|
|
1093
|
+
}
|
|
760
1094
|
/**
|
|
761
1095
|
* Clear the active account
|
|
762
1096
|
*/
|
|
@@ -846,32 +1180,16 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
846
1180
|
};
|
|
847
1181
|
});
|
|
848
1182
|
}
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
});
|
|
858
|
-
}
|
|
859
|
-
sendMetrics(uri, options, thenHandler, catchHandler) {
|
|
860
|
-
if (!this.enableMetrics && uri === 'performance-metrics/save') {
|
|
861
|
-
options && this.updateMetricsStorage(options.body);
|
|
862
|
-
}
|
|
863
|
-
if (!this.enableMetrics) {
|
|
864
|
-
return;
|
|
865
|
-
}
|
|
866
|
-
fetch(`${octez_connect_core_1.BACKEND_URL}/${uri}`, options)
|
|
867
|
-
.then((res) => thenHandler && thenHandler(res))
|
|
868
|
-
.catch((err) => {
|
|
869
|
-
console.warn('Network error encountered. Metrics sharing have been automatically disabled.');
|
|
870
|
-
logger.error(err.message);
|
|
871
|
-
this.enableMetrics = false; // in the event of a network error, stop sending metrics
|
|
872
|
-
catchHandler && catchHandler(err);
|
|
873
|
-
});
|
|
1183
|
+
// Metrics phone home to a Papers-controlled endpoint that the ECAD fork
|
|
1184
|
+
// does not control or ingest. The disabled path also queued payloads into
|
|
1185
|
+
// IndexedDB before the DB was ready, surfacing as unhandledRejection.
|
|
1186
|
+
// Hard-disabled here; full removal (option, storage key, IDB store,
|
|
1187
|
+
// BACKEND_URL constant, all call sites) tracked as a follow-up.
|
|
1188
|
+
/* eslint-disable @typescript-eslint/no-unused-vars -- hard-disabled stub keeps its signature for call-site type-safety; params are intentionally unused pending full removal. */
|
|
1189
|
+
sendMetrics(_uri, _options, _thenHandler, _catchHandler) {
|
|
1190
|
+
return;
|
|
874
1191
|
}
|
|
1192
|
+
/* eslint-enable @typescript-eslint/no-unused-vars */
|
|
875
1193
|
checkMakeRequest() {
|
|
876
1194
|
return __awaiter(this, void 0, void 0, function* () {
|
|
877
1195
|
const isResolved = this._transport.isResolved();
|
|
@@ -931,14 +1249,19 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
931
1249
|
* Remove all peers and all accounts that have been connected through those peers
|
|
932
1250
|
*/
|
|
933
1251
|
removeAllPeers() {
|
|
934
|
-
return __awaiter(this, arguments, void 0, function* (sendDisconnectToPeers = false) {
|
|
935
|
-
const transport = yield this.transport;
|
|
1252
|
+
return __awaiter(this, arguments, void 0, function* (sendDisconnectToPeers = false, options = {}) {
|
|
1253
|
+
const transport = (yield this.transport);
|
|
936
1254
|
const peers = yield transport.getPeers();
|
|
937
|
-
const removePeerResult = transport.removeAllPeers();
|
|
1255
|
+
const removePeerResult = yield transport.removeAllPeers();
|
|
938
1256
|
yield this.removeAccountsForPeers(peers);
|
|
939
1257
|
if (sendDisconnectToPeers) {
|
|
940
1258
|
const disconnectPromises = peers.map((peer) => this.sendDisconnectToPeer(peer, transport));
|
|
941
|
-
|
|
1259
|
+
if (options.ignoreDisconnectErrors) {
|
|
1260
|
+
yield Promise.allSettled(disconnectPromises);
|
|
1261
|
+
}
|
|
1262
|
+
else {
|
|
1263
|
+
yield Promise.all(disconnectPromises);
|
|
1264
|
+
}
|
|
942
1265
|
}
|
|
943
1266
|
return removePeerResult;
|
|
944
1267
|
});
|
|
@@ -1044,13 +1367,15 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
1044
1367
|
this.sendMetrics('performance-metrics/save', yield this.buildPayload('connect', 'start'));
|
|
1045
1368
|
const logId = `makeRequestV3 ${Date.now()}`;
|
|
1046
1369
|
logger.time(true, logId);
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1370
|
+
let resolved;
|
|
1371
|
+
try {
|
|
1372
|
+
resolved = yield this.makeRequestV3(request);
|
|
1373
|
+
}
|
|
1374
|
+
catch (requestError) {
|
|
1375
|
+
yield this.runRequestErrorSideEffects(request, requestError, logId);
|
|
1376
|
+
throw requestError;
|
|
1377
|
+
}
|
|
1378
|
+
const { message: response, connectionInfo } = resolved;
|
|
1054
1379
|
logger.time(false, logId);
|
|
1055
1380
|
this.sendMetrics('performance-metrics/save', yield this.buildPayload('connect', 'start'));
|
|
1056
1381
|
logger.log('RESPONSE V3', response, connectionInfo);
|
|
@@ -1112,14 +1437,18 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
1112
1437
|
const res = (yield this.checkMakeRequest())
|
|
1113
1438
|
? this.makeRequestV3(request)
|
|
1114
1439
|
: this.makeRequestBC(request);
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1440
|
+
let resolved;
|
|
1441
|
+
try {
|
|
1442
|
+
resolved = yield res;
|
|
1443
|
+
}
|
|
1444
|
+
catch (requestError) {
|
|
1445
|
+
yield this.runRequestErrorSideEffects(request, requestError, logId);
|
|
1446
|
+
throw requestError;
|
|
1447
|
+
}
|
|
1448
|
+
if (!resolved) {
|
|
1449
|
+
throw new Error('Internal error: makeRequest returned no result');
|
|
1450
|
+
}
|
|
1451
|
+
const { message: response, connectionInfo } = resolved;
|
|
1123
1452
|
logger.time(false, logId);
|
|
1124
1453
|
this.sendMetrics('performance-metrics/save', yield this.buildPayload('message', 'success'));
|
|
1125
1454
|
yield blockchain.handleResponse({
|
|
@@ -1145,16 +1474,36 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
1145
1474
|
*/
|
|
1146
1475
|
requestPermissions(input) {
|
|
1147
1476
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1148
|
-
|
|
1149
|
-
|
|
1477
|
+
this.assertNotDestroyed();
|
|
1478
|
+
const scopes = this.getPermissionRequestScopes(input);
|
|
1479
|
+
const requestPermissionsKey = this.getPermissionRequestKey(scopes);
|
|
1480
|
+
if (this._requestPermissionsPromise) {
|
|
1481
|
+
if (this._requestPermissionsKey !== requestPermissionsKey) {
|
|
1482
|
+
throw new Error('Cannot start a permission request with different scopes while another permission request is pending');
|
|
1483
|
+
}
|
|
1484
|
+
return this._requestPermissionsPromise;
|
|
1485
|
+
}
|
|
1486
|
+
const requestPermissionsPromise = this.requestPermissionsInternal(scopes);
|
|
1487
|
+
this._requestPermissionsPromise = requestPermissionsPromise;
|
|
1488
|
+
this._requestPermissionsKey = requestPermissionsKey;
|
|
1489
|
+
try {
|
|
1490
|
+
return yield requestPermissionsPromise;
|
|
1150
1491
|
}
|
|
1492
|
+
finally {
|
|
1493
|
+
if (this._requestPermissionsPromise === requestPermissionsPromise) {
|
|
1494
|
+
this._requestPermissionsPromise = undefined;
|
|
1495
|
+
this._requestPermissionsKey = undefined;
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
});
|
|
1499
|
+
}
|
|
1500
|
+
requestPermissionsInternal(scopes) {
|
|
1501
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1151
1502
|
const request = {
|
|
1152
1503
|
appMetadata: yield this.getOwnAppMetadata(),
|
|
1153
1504
|
type: octez_connect_types_1.BeaconMessageType.PermissionRequest,
|
|
1154
1505
|
network: this.network,
|
|
1155
|
-
scopes
|
|
1156
|
-
? input.scopes
|
|
1157
|
-
: [octez_connect_types_1.PermissionScope.OPERATION_REQUEST, octez_connect_types_1.PermissionScope.SIGN]
|
|
1506
|
+
scopes
|
|
1158
1507
|
};
|
|
1159
1508
|
this.analytics.track('event', 'DAppClient', 'Permission requested');
|
|
1160
1509
|
this.sendMetrics('performance-metrics/save', yield this.buildPayload('connect', 'start'));
|
|
@@ -1163,14 +1512,18 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
1163
1512
|
const res = (yield this.checkMakeRequest()) || !(yield this.getActiveAccount())
|
|
1164
1513
|
? this.makeRequest(request, undefined, undefined)
|
|
1165
1514
|
: this.makeRequestBC(request);
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1515
|
+
let resolved;
|
|
1516
|
+
try {
|
|
1517
|
+
resolved = yield res;
|
|
1518
|
+
}
|
|
1519
|
+
catch (requestError) {
|
|
1520
|
+
yield this.runRequestErrorSideEffects(request, requestError, logId);
|
|
1521
|
+
throw requestError;
|
|
1522
|
+
}
|
|
1523
|
+
if (!resolved) {
|
|
1524
|
+
throw new Error('Internal error: makeRequest returned no result');
|
|
1525
|
+
}
|
|
1526
|
+
const { message, connectionInfo } = resolved;
|
|
1174
1527
|
logger.time(false, logId);
|
|
1175
1528
|
this.sendMetrics('performance-metrics/save', yield this.buildPayload('connect', 'success'));
|
|
1176
1529
|
logger.log('requestPermissions', '######## MESSAGE #######');
|
|
@@ -1193,6 +1546,17 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
1193
1546
|
return output;
|
|
1194
1547
|
});
|
|
1195
1548
|
}
|
|
1549
|
+
getPermissionRequestScopes(input) {
|
|
1550
|
+
if (input && 'network' in input) {
|
|
1551
|
+
throw new Error('[BEACON] the "network" property is no longer accepted in input. Please provide it when instantiating DAppClient.');
|
|
1552
|
+
}
|
|
1553
|
+
return input && input.scopes
|
|
1554
|
+
? input.scopes
|
|
1555
|
+
: [octez_connect_types_1.PermissionScope.OPERATION_REQUEST, octez_connect_types_1.PermissionScope.SIGN];
|
|
1556
|
+
}
|
|
1557
|
+
getPermissionRequestKey(scopes) {
|
|
1558
|
+
return [...scopes].sort().join('|');
|
|
1559
|
+
}
|
|
1196
1560
|
/**
|
|
1197
1561
|
* Send a proof of event request to the wallet. The wallet will either accept or decline the challenge.
|
|
1198
1562
|
* If it is accepted, the challenge will be stored, meaning that even if the user refresh the page, the DAppClient will keep checking if the challenge has been fulfilled.
|
|
@@ -1220,14 +1584,18 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
1220
1584
|
const res = (yield this.checkMakeRequest())
|
|
1221
1585
|
? this.makeRequest(request)
|
|
1222
1586
|
: this.makeRequestBC(request);
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1587
|
+
let resolved;
|
|
1588
|
+
try {
|
|
1589
|
+
resolved = yield res;
|
|
1590
|
+
}
|
|
1591
|
+
catch (requestError) {
|
|
1592
|
+
yield this.runRequestErrorSideEffects(request, requestError, logId);
|
|
1593
|
+
throw requestError;
|
|
1594
|
+
}
|
|
1595
|
+
if (!resolved) {
|
|
1596
|
+
throw new Error('Internal error: makeRequest returned no result');
|
|
1597
|
+
}
|
|
1598
|
+
const { message, connectionInfo } = resolved;
|
|
1231
1599
|
logger.time(false, logId);
|
|
1232
1600
|
this.sendMetrics('performance-metrics/save', yield this.buildPayload('message', 'success'));
|
|
1233
1601
|
this.analytics.track('event', 'DAppClient', `Proof of event challenge ${message.isAccepted ? 'accepted' : 'refused'}`, { address: activeAccount.address });
|
|
@@ -1264,14 +1632,18 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
1264
1632
|
const res = (yield this.checkMakeRequest())
|
|
1265
1633
|
? this.makeRequest(request)
|
|
1266
1634
|
: this.makeRequestBC(request);
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1635
|
+
let resolved;
|
|
1636
|
+
try {
|
|
1637
|
+
resolved = yield res;
|
|
1638
|
+
}
|
|
1639
|
+
catch (requestError) {
|
|
1640
|
+
yield this.runRequestErrorSideEffects(request, requestError, logId);
|
|
1641
|
+
throw requestError;
|
|
1642
|
+
}
|
|
1643
|
+
if (!resolved) {
|
|
1644
|
+
throw new Error('Internal error: makeRequest returned no result');
|
|
1645
|
+
}
|
|
1646
|
+
const { message, connectionInfo } = resolved;
|
|
1275
1647
|
logger.time(false, logId);
|
|
1276
1648
|
this.analytics.track('event', 'DAppClient', `Simulated proof of event challenge ${!message.errorMessage ? 'accepted' : 'refused'}`, { address: activeAccount.address });
|
|
1277
1649
|
yield this.notifySuccess(request, {
|
|
@@ -1333,14 +1705,18 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
1333
1705
|
const res = (yield this.checkMakeRequest())
|
|
1334
1706
|
? this.makeRequest(request)
|
|
1335
1707
|
: this.makeRequestBC(request);
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1708
|
+
let resolved;
|
|
1709
|
+
try {
|
|
1710
|
+
resolved = yield res;
|
|
1711
|
+
}
|
|
1712
|
+
catch (requestError) {
|
|
1713
|
+
yield this.runRequestErrorSideEffects(request, requestError, logId);
|
|
1714
|
+
throw requestError;
|
|
1715
|
+
}
|
|
1716
|
+
if (!resolved) {
|
|
1717
|
+
throw new Error('Internal error: makeRequest returned no result');
|
|
1718
|
+
}
|
|
1719
|
+
const { message, connectionInfo } = resolved;
|
|
1344
1720
|
logger.time(false, logId);
|
|
1345
1721
|
this.sendMetrics('performance-metrics/save', yield this.buildPayload('message', 'success'));
|
|
1346
1722
|
yield this.notifySuccess(request, {
|
|
@@ -1430,14 +1806,18 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
1430
1806
|
const res = (yield this.checkMakeRequest())
|
|
1431
1807
|
? this.makeRequest(request)
|
|
1432
1808
|
: this.makeRequestBC(request);
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1809
|
+
let resolved;
|
|
1810
|
+
try {
|
|
1811
|
+
resolved = yield res;
|
|
1812
|
+
}
|
|
1813
|
+
catch (requestError) {
|
|
1814
|
+
yield this.runRequestErrorSideEffects(request, requestError, logId);
|
|
1815
|
+
throw requestError;
|
|
1816
|
+
}
|
|
1817
|
+
if (!resolved) {
|
|
1818
|
+
throw new Error('Internal error: makeRequest returned no result');
|
|
1819
|
+
}
|
|
1820
|
+
const { message, connectionInfo } = resolved;
|
|
1441
1821
|
logger.time(false, logId);
|
|
1442
1822
|
this.sendMetrics('performance-metrics/save', yield this.buildPayload('message', 'success'));
|
|
1443
1823
|
yield this.notifySuccess(request, {
|
|
@@ -1480,14 +1860,18 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
1480
1860
|
const res = (yield this.checkMakeRequest())
|
|
1481
1861
|
? this.makeRequest(request)
|
|
1482
1862
|
: this.makeRequestBC(request);
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1863
|
+
let resolved;
|
|
1864
|
+
try {
|
|
1865
|
+
resolved = yield res;
|
|
1866
|
+
}
|
|
1867
|
+
catch (requestError) {
|
|
1868
|
+
yield this.runRequestErrorSideEffects(request, requestError, logId);
|
|
1869
|
+
throw requestError;
|
|
1870
|
+
}
|
|
1871
|
+
if (!resolved) {
|
|
1872
|
+
throw new Error('Internal error: makeRequest returned no result');
|
|
1873
|
+
}
|
|
1874
|
+
const { message, connectionInfo } = resolved;
|
|
1491
1875
|
logger.time(false, logId);
|
|
1492
1876
|
this.sendMetrics('performance-metrics/save', yield this.buildPayload('message', 'success'));
|
|
1493
1877
|
yield this.notifySuccess(request, {
|
|
@@ -1561,13 +1945,17 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
1561
1945
|
*/
|
|
1562
1946
|
removeAccountsForPeers(peersToRemove) {
|
|
1563
1947
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1948
|
+
if (peersToRemove.length === 0) {
|
|
1949
|
+
return;
|
|
1950
|
+
}
|
|
1564
1951
|
const peerIdsToRemove = peersToRemove.map((peer) => peer.senderId);
|
|
1565
1952
|
return this.removeAccountsForPeerIds(peerIdsToRemove);
|
|
1566
1953
|
});
|
|
1567
1954
|
}
|
|
1568
1955
|
removeAccountsForPeerIds(peerIds) {
|
|
1569
1956
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1570
|
-
|
|
1957
|
+
var _a;
|
|
1958
|
+
const accounts = (_a = (yield this.accountManager.getAccounts())) !== null && _a !== void 0 ? _a : [];
|
|
1571
1959
|
// Remove all accounts with origin of the specified peer
|
|
1572
1960
|
const accountsToRemove = accounts.filter((account) => peerIds.includes(account.senderId));
|
|
1573
1961
|
const accountIdentifiersToRemove = accountsToRemove.map((accountInfo) => accountInfo.accountIdentifier);
|
|
@@ -1581,6 +1969,46 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
1581
1969
|
}
|
|
1582
1970
|
});
|
|
1583
1971
|
}
|
|
1972
|
+
/**
|
|
1973
|
+
* Run the standard request-error side effects (abort/error metric,
|
|
1974
|
+
* timer stop, {@link handleRequestError}) on a single awaited control
|
|
1975
|
+
* flow so callers can re-throw the original {@link ErrorResponse} without
|
|
1976
|
+
* leaking an UnhandledPromiseRejection.
|
|
1977
|
+
*
|
|
1978
|
+
* Why this exists: every request method historically used a fire-and-
|
|
1979
|
+
* forget `res.catch(handler)` whose handler did `throw await
|
|
1980
|
+
* this.handleRequestError(...)`. The catch returned a detached promise
|
|
1981
|
+
* nobody awaited; `handleRequestError`'s thrown wrapped error became an
|
|
1982
|
+
* unhandled rejection. The bug rarely surfaced before the dapp-init
|
|
1983
|
+
* promise was made rejectable, because pre-pairing rejection paths used
|
|
1984
|
+
* to hang instead of routing through these handlers. Once init started
|
|
1985
|
+
* rejecting reliably, the unhandled rejection started firing on every
|
|
1986
|
+
* WC session-proposal rejection. We swallow the wrapped throw here so
|
|
1987
|
+
* callers can re-throw the original `ErrorResponse`, matching the
|
|
1988
|
+
* post-pairing rejection contract at the openRequest path.
|
|
1989
|
+
*
|
|
1990
|
+
* @param request The request we sent
|
|
1991
|
+
* @param requestError The error we received
|
|
1992
|
+
* @param logId The {@link logger.time} label opened for this request
|
|
1993
|
+
*/
|
|
1994
|
+
runRequestErrorSideEffects(request, rawError, logId) {
|
|
1995
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1996
|
+
// V3 request shapes (PermissionRequestV3, BlockchainRequestV3) flow through
|
|
1997
|
+
// here too. They aren't part of BeaconRequestInputMessage, but
|
|
1998
|
+
// handleRequestError only inspects `.type` plus optional fields it already
|
|
1999
|
+
// guards on. Catch params are `unknown` under strict TS, same story.
|
|
2000
|
+
const typedRequest = request;
|
|
2001
|
+
const requestError = rawError;
|
|
2002
|
+
if (requestError.errorType === octez_connect_types_1.BeaconErrorType.ABORTED_ERROR) {
|
|
2003
|
+
this.sendMetrics('performance-metrics/save', yield this.buildPayload('message', 'abort'));
|
|
2004
|
+
}
|
|
2005
|
+
else {
|
|
2006
|
+
this.sendMetrics('performance-metrics/save', yield this.buildPayload('message', 'error'));
|
|
2007
|
+
}
|
|
2008
|
+
logger.time(false, logId);
|
|
2009
|
+
yield this.handleRequestError(typedRequest, requestError).catch(() => undefined);
|
|
2010
|
+
});
|
|
2011
|
+
}
|
|
1584
2012
|
/**
|
|
1585
2013
|
* This message handles errors that we receive from the wallet.
|
|
1586
2014
|
*
|
|
@@ -1763,20 +2191,10 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
1763
2191
|
}
|
|
1764
2192
|
makeRequest(requestInput, skipResponse, otherTabMessageId) {
|
|
1765
2193
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1766
|
-
var _a
|
|
2194
|
+
var _a;
|
|
1767
2195
|
const messageId = otherTabMessageId !== null && otherTabMessageId !== void 0 ? otherTabMessageId : (yield (0, octez_connect_utils_1.generateGUID)());
|
|
1768
|
-
if (this._initPromise && this.isInitPending) {
|
|
1769
|
-
yield Promise.all([
|
|
1770
|
-
(_a = this.postMessageTransport) === null || _a === void 0 ? void 0 : _a.disconnect(),
|
|
1771
|
-
(_b = this.walletConnectTransport) === null || _b === void 0 ? void 0 : _b.disconnect()
|
|
1772
|
-
]);
|
|
1773
|
-
this._initPromise = undefined;
|
|
1774
|
-
this.hideUI(['toast']);
|
|
1775
|
-
}
|
|
1776
2196
|
logger.log('makeRequest', 'starting');
|
|
1777
|
-
this.isInitPending = true;
|
|
1778
2197
|
yield this.init();
|
|
1779
|
-
this.isInitPending = false;
|
|
1780
2198
|
logger.log('makeRequest', 'after init');
|
|
1781
2199
|
if (yield this.addRequestAndCheckIfRateLimited()) {
|
|
1782
2200
|
this.events
|
|
@@ -1797,20 +2215,34 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
1797
2215
|
exposed = new octez_connect_utils_1.ExposedPromise();
|
|
1798
2216
|
this.addOpenRequest(request.id, exposed);
|
|
1799
2217
|
}
|
|
1800
|
-
const payload = yield new octez_connect_core_1.Serializer().serialize(request);
|
|
1801
|
-
const account = yield this.getActiveAccount();
|
|
1802
|
-
const peer = yield this.getPeer(account);
|
|
1803
|
-
const walletInfo = yield this.getWalletInfo(peer, account);
|
|
1804
|
-
logger.log('makeRequest', 'sending message', request);
|
|
1805
2218
|
try {
|
|
1806
|
-
;
|
|
1807
|
-
|
|
2219
|
+
const payload = yield new octez_connect_core_1.Serializer().serialize(request);
|
|
2220
|
+
const account = yield this.getActiveAccount();
|
|
2221
|
+
const peer = yield this.getPeer(account);
|
|
2222
|
+
const walletInfo = yield this.getWalletInfo(peer, account);
|
|
2223
|
+
logger.log('makeRequest', 'sending message', request);
|
|
2224
|
+
yield (yield this.transport).send(payload, peer);
|
|
1808
2225
|
if (request.type !== octez_connect_types_1.BeaconMessageType.PermissionRequest ||
|
|
1809
2226
|
(this._activeAccount.isResolved() && (yield this._activeAccount.promise))) {
|
|
1810
2227
|
this.tryToAppSwitch();
|
|
1811
2228
|
}
|
|
2229
|
+
if (!otherTabMessageId) {
|
|
2230
|
+
this.events
|
|
2231
|
+
.emit(beacon_message_events_1.messageEvents[requestInput.type].sent, {
|
|
2232
|
+
walletInfo: Object.assign(Object.assign({}, walletInfo), { name: (_a = walletInfo.name) !== null && _a !== void 0 ? _a : 'Wallet' }),
|
|
2233
|
+
extraInfo: {
|
|
2234
|
+
resetCallback: () => __awaiter(this, void 0, void 0, function* () {
|
|
2235
|
+
yield this.disconnect();
|
|
2236
|
+
})
|
|
2237
|
+
}
|
|
2238
|
+
})
|
|
2239
|
+
.catch((emitError) => logger.warn('makeRequest', emitError));
|
|
2240
|
+
}
|
|
1812
2241
|
}
|
|
1813
2242
|
catch (sendError) {
|
|
2243
|
+
if (!skipResponse) {
|
|
2244
|
+
this.clearOpenRequest(request.id);
|
|
2245
|
+
}
|
|
1814
2246
|
this.events.emit(events_1.BeaconEvent.INTERNAL_ERROR, {
|
|
1815
2247
|
text: 'Unable to send message. If this problem persists, please reset the connection and pair your wallet again.',
|
|
1816
2248
|
buttons: [
|
|
@@ -1825,18 +2257,6 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
1825
2257
|
});
|
|
1826
2258
|
throw sendError;
|
|
1827
2259
|
}
|
|
1828
|
-
if (!otherTabMessageId) {
|
|
1829
|
-
this.events
|
|
1830
|
-
.emit(beacon_message_events_1.messageEvents[requestInput.type].sent, {
|
|
1831
|
-
walletInfo: Object.assign(Object.assign({}, walletInfo), { name: (_c = walletInfo.name) !== null && _c !== void 0 ? _c : 'Wallet' }),
|
|
1832
|
-
extraInfo: {
|
|
1833
|
-
resetCallback: () => __awaiter(this, void 0, void 0, function* () {
|
|
1834
|
-
this.disconnect();
|
|
1835
|
-
})
|
|
1836
|
-
}
|
|
1837
|
-
})
|
|
1838
|
-
.catch((emitError) => console.warn(emitError));
|
|
1839
|
-
}
|
|
1840
2260
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1841
2261
|
return exposed === null || exposed === void 0 ? void 0 : exposed.promise; // TODO: fix type
|
|
1842
2262
|
});
|
|
@@ -1851,20 +2271,10 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
1851
2271
|
*/
|
|
1852
2272
|
makeRequestV3(requestInput, otherTabMessageId) {
|
|
1853
2273
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1854
|
-
var _a
|
|
1855
|
-
if (this._initPromise && this.isInitPending) {
|
|
1856
|
-
yield Promise.all([
|
|
1857
|
-
(_a = this.postMessageTransport) === null || _a === void 0 ? void 0 : _a.disconnect(),
|
|
1858
|
-
(_b = this.walletConnectTransport) === null || _b === void 0 ? void 0 : _b.disconnect()
|
|
1859
|
-
]);
|
|
1860
|
-
this._initPromise = undefined;
|
|
1861
|
-
this.hideUI(['toast']);
|
|
1862
|
-
}
|
|
2274
|
+
var _a;
|
|
1863
2275
|
const messageId = otherTabMessageId !== null && otherTabMessageId !== void 0 ? otherTabMessageId : (yield (0, octez_connect_utils_1.generateGUID)());
|
|
1864
2276
|
logger.log('makeRequest', 'starting');
|
|
1865
|
-
this.isInitPending = true;
|
|
1866
2277
|
yield this.init(undefined, true);
|
|
1867
|
-
this.isInitPending = false;
|
|
1868
2278
|
logger.log('makeRequest', 'after init');
|
|
1869
2279
|
if (yield this.addRequestAndCheckIfRateLimited()) {
|
|
1870
2280
|
this.events
|
|
@@ -1883,20 +2293,31 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
1883
2293
|
};
|
|
1884
2294
|
const exposed = new octez_connect_utils_1.ExposedPromise();
|
|
1885
2295
|
this.addOpenRequest(request.id, exposed);
|
|
1886
|
-
const payload = yield new octez_connect_core_1.Serializer().serialize(request);
|
|
1887
|
-
const account = yield this.getActiveAccount();
|
|
1888
|
-
const peer = yield this.getPeer(account);
|
|
1889
|
-
const walletInfo = yield this.getWalletInfo(peer, account);
|
|
1890
|
-
logger.log('makeRequest', 'sending message', request);
|
|
1891
2296
|
try {
|
|
1892
|
-
;
|
|
1893
|
-
|
|
2297
|
+
const payload = yield new octez_connect_core_1.Serializer().serialize(request);
|
|
2298
|
+
const account = yield this.getActiveAccount();
|
|
2299
|
+
const peer = yield this.getPeer(account);
|
|
2300
|
+
const walletInfo = yield this.getWalletInfo(peer, account);
|
|
2301
|
+
logger.log('makeRequest', 'sending message', request);
|
|
2302
|
+
yield (yield this.transport).send(payload, peer);
|
|
1894
2303
|
if (request.message.type !== octez_connect_types_1.BeaconMessageType.PermissionRequest ||
|
|
1895
2304
|
(this._activeAccount.isResolved() && (yield this._activeAccount.promise))) {
|
|
1896
2305
|
this.tryToAppSwitch();
|
|
1897
2306
|
}
|
|
2307
|
+
const index = requestInput.type;
|
|
2308
|
+
this.events
|
|
2309
|
+
.emit(beacon_message_events_1.messageEvents[index].sent, {
|
|
2310
|
+
walletInfo: Object.assign(Object.assign({}, walletInfo), { name: (_a = walletInfo.name) !== null && _a !== void 0 ? _a : 'Wallet' }),
|
|
2311
|
+
extraInfo: {
|
|
2312
|
+
resetCallback: () => __awaiter(this, void 0, void 0, function* () {
|
|
2313
|
+
yield this.disconnect();
|
|
2314
|
+
})
|
|
2315
|
+
}
|
|
2316
|
+
})
|
|
2317
|
+
.catch((emitError) => logger.warn('makeRequestV3', emitError));
|
|
1898
2318
|
}
|
|
1899
2319
|
catch (sendError) {
|
|
2320
|
+
this.clearOpenRequest(request.id);
|
|
1900
2321
|
this.events.emit(events_1.BeaconEvent.INTERNAL_ERROR, {
|
|
1901
2322
|
text: 'Unable to send message. If this problem persists, please reset the connection and pair your wallet again.',
|
|
1902
2323
|
buttons: [
|
|
@@ -1911,17 +2332,6 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
1911
2332
|
});
|
|
1912
2333
|
throw sendError;
|
|
1913
2334
|
}
|
|
1914
|
-
const index = requestInput.type;
|
|
1915
|
-
this.events
|
|
1916
|
-
.emit(beacon_message_events_1.messageEvents[index].sent, {
|
|
1917
|
-
walletInfo: Object.assign(Object.assign({}, walletInfo), { name: (_c = walletInfo.name) !== null && _c !== void 0 ? _c : 'Wallet' }),
|
|
1918
|
-
extraInfo: {
|
|
1919
|
-
resetCallback: () => __awaiter(this, void 0, void 0, function* () {
|
|
1920
|
-
this.disconnect();
|
|
1921
|
-
})
|
|
1922
|
-
}
|
|
1923
|
-
})
|
|
1924
|
-
.catch((emitError) => console.warn(emitError));
|
|
1925
2335
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1926
2336
|
return exposed.promise; // TODO: fix type
|
|
1927
2337
|
});
|
|
@@ -1964,27 +2374,149 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
1964
2374
|
return exposed.promise;
|
|
1965
2375
|
});
|
|
1966
2376
|
}
|
|
2377
|
+
/**
|
|
2378
|
+
* Disconnects all resolved transports (postMessage, P2P, WalletConnect), removes their peers,
|
|
2379
|
+
* and clears active account state. After calling, the DAppClient remains usable for a new
|
|
2380
|
+
* requestPermissions().
|
|
2381
|
+
*/
|
|
1967
2382
|
disconnect() {
|
|
2383
|
+
return __awaiter(this, arguments, void 0, function* (options = {}) {
|
|
2384
|
+
const { notifyPeers = true } = options;
|
|
2385
|
+
if (!this._disconnectPromise) {
|
|
2386
|
+
this._disconnectNotifyPeers = false;
|
|
2387
|
+
}
|
|
2388
|
+
this._disconnectNotifyPeers = this._disconnectNotifyPeers || notifyPeers;
|
|
2389
|
+
if (this._disconnectPromise) {
|
|
2390
|
+
return this._disconnectPromise;
|
|
2391
|
+
}
|
|
2392
|
+
this._disconnectPromise = (() => __awaiter(this, void 0, void 0, function* () { return this.disconnectInternal(); }))().finally(() => {
|
|
2393
|
+
this._disconnectPromise = undefined;
|
|
2394
|
+
this._disconnectNotifyPeers = false;
|
|
2395
|
+
});
|
|
2396
|
+
return this._disconnectPromise;
|
|
2397
|
+
});
|
|
2398
|
+
}
|
|
2399
|
+
disconnectInternal() {
|
|
1968
2400
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2401
|
+
this.abortPendingInit();
|
|
1969
2402
|
if (!this._transport.isResolved()) {
|
|
1970
|
-
|
|
2403
|
+
const initializedTransports = this.getInitializedTransports();
|
|
2404
|
+
this.abortOpenRequests();
|
|
2405
|
+
yield this.removeAllPeersFromTransports(initializedTransports);
|
|
2406
|
+
yield this.clearActiveAccount();
|
|
2407
|
+
yield this.disconnectResolvedTransports(initializedTransports);
|
|
2408
|
+
this.clearInternalTransportReferences();
|
|
2409
|
+
return;
|
|
1971
2410
|
}
|
|
1972
|
-
const transport = yield this.transport;
|
|
2411
|
+
const transport = (yield this.transport);
|
|
1973
2412
|
if (transport.connectionStatus === octez_connect_types_1.TransportStatus.NOT_CONNECTED) {
|
|
1974
|
-
|
|
2413
|
+
yield this.clearActiveAccount();
|
|
2414
|
+
this.abortOpenRequests();
|
|
2415
|
+
return;
|
|
1975
2416
|
}
|
|
1976
2417
|
yield this.createStateSnapshot();
|
|
1977
2418
|
this.sendMetrics('performance-metrics/save', yield this.buildPayload('disconnect', 'start'));
|
|
2419
|
+
this.abortOpenRequests();
|
|
2420
|
+
const transports = this.getResolvedTransports(transport);
|
|
2421
|
+
yield this.removeAllPeersFromTransports(transports);
|
|
1978
2422
|
yield this.clearActiveAccount();
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
}
|
|
1982
|
-
this.postMessageTransport = undefined;
|
|
1983
|
-
this.p2pTransport = undefined;
|
|
1984
|
-
this.walletConnectTransport = undefined;
|
|
2423
|
+
yield this.disconnectResolvedTransports(transports);
|
|
2424
|
+
this.clearInternalTransportReferences();
|
|
1985
2425
|
this.sendMetrics('performance-metrics/save', yield this.buildPayload('disconnect', 'success'));
|
|
1986
2426
|
});
|
|
1987
2427
|
}
|
|
2428
|
+
getInitializedTransports() {
|
|
2429
|
+
const transports = [];
|
|
2430
|
+
const addTransport = (transport) => {
|
|
2431
|
+
if (transport && !transports.includes(transport)) {
|
|
2432
|
+
transports.push(transport);
|
|
2433
|
+
}
|
|
2434
|
+
};
|
|
2435
|
+
addTransport(this.postMessageTransport);
|
|
2436
|
+
addTransport(this.p2pTransport);
|
|
2437
|
+
addTransport(this.walletConnectTransport);
|
|
2438
|
+
return transports;
|
|
2439
|
+
}
|
|
2440
|
+
getResolvedTransports(selectedTransport) {
|
|
2441
|
+
const transports = this.getInitializedTransports();
|
|
2442
|
+
const addTransport = (transport) => {
|
|
2443
|
+
if (transport && !transports.includes(transport)) {
|
|
2444
|
+
transports.push(transport);
|
|
2445
|
+
}
|
|
2446
|
+
};
|
|
2447
|
+
addTransport(this.postMessageTransport);
|
|
2448
|
+
addTransport(this.p2pTransport);
|
|
2449
|
+
addTransport(this.walletConnectTransport);
|
|
2450
|
+
addTransport(selectedTransport);
|
|
2451
|
+
return transports;
|
|
2452
|
+
}
|
|
2453
|
+
clearInternalTransportReferences() {
|
|
2454
|
+
this.postMessageTransport = undefined;
|
|
2455
|
+
this.p2pTransport = undefined;
|
|
2456
|
+
this.walletConnectTransport = undefined;
|
|
2457
|
+
}
|
|
2458
|
+
removeAllPeersFromTransports(transports) {
|
|
2459
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2460
|
+
const peersByTransport = yield Promise.all(transports.map((transport) => __awaiter(this, void 0, void 0, function* () {
|
|
2461
|
+
const transportPeers = yield transport.getPeers();
|
|
2462
|
+
return {
|
|
2463
|
+
transport,
|
|
2464
|
+
peers: transportPeers
|
|
2465
|
+
};
|
|
2466
|
+
})));
|
|
2467
|
+
yield Promise.all(peersByTransport.map(({ transport }) => transport.removeAllPeers()));
|
|
2468
|
+
const peersToRemove = peersByTransport.flatMap(({ peers: transportPeers }) => transportPeers);
|
|
2469
|
+
yield this.removeAccountsForPeers(peersToRemove);
|
|
2470
|
+
// If cleanup already removed peers, a late notify upgrade has nothing left to notify.
|
|
2471
|
+
const sendDisconnectToPeers = this._disconnectNotifyPeers;
|
|
2472
|
+
if (!sendDisconnectToPeers) {
|
|
2473
|
+
return;
|
|
2474
|
+
}
|
|
2475
|
+
yield Promise.allSettled(this.getUniquePeerNotifications(peersByTransport).map(({ transport, peer }) => this.sendDisconnectToPeer(peer, transport)));
|
|
2476
|
+
});
|
|
2477
|
+
}
|
|
2478
|
+
getUniquePeerNotifications(peersByTransport) {
|
|
2479
|
+
const notifications = [];
|
|
2480
|
+
const seen = new Set();
|
|
2481
|
+
peersByTransport.forEach(({ transport, peers: transportPeers }) => {
|
|
2482
|
+
transportPeers.forEach((peer) => {
|
|
2483
|
+
const key = this.getPeerNotificationKey(transport, peer);
|
|
2484
|
+
if (seen.has(key)) {
|
|
2485
|
+
return;
|
|
2486
|
+
}
|
|
2487
|
+
seen.add(key);
|
|
2488
|
+
notifications.push({ transport, peer });
|
|
2489
|
+
});
|
|
2490
|
+
});
|
|
2491
|
+
return notifications;
|
|
2492
|
+
}
|
|
2493
|
+
getPeerNotificationKey(transport, peer) {
|
|
2494
|
+
return [transport.type, peer.publicKey, peer.senderId, peer.id].filter(Boolean).join(':');
|
|
2495
|
+
}
|
|
2496
|
+
disconnectResolvedTransports(transports) {
|
|
2497
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2498
|
+
const results = yield Promise.allSettled(transports.map((transport) => __awaiter(this, void 0, void 0, function* () {
|
|
2499
|
+
if (this.isTransportConnected(transport)) {
|
|
2500
|
+
yield transport.disconnect();
|
|
2501
|
+
}
|
|
2502
|
+
if (this.hasClientCleanup(transport)) {
|
|
2503
|
+
yield transport.doClientCleanup();
|
|
2504
|
+
}
|
|
2505
|
+
})));
|
|
2506
|
+
results.forEach((result) => {
|
|
2507
|
+
if (result.status === 'rejected') {
|
|
2508
|
+
logger.warn('disconnectResolvedTransports', result.reason);
|
|
2509
|
+
}
|
|
2510
|
+
});
|
|
2511
|
+
});
|
|
2512
|
+
}
|
|
2513
|
+
hasClientCleanup(transport) {
|
|
2514
|
+
const transportWithClientCleanup = transport;
|
|
2515
|
+
return typeof transportWithClientCleanup.doClientCleanup === 'function';
|
|
2516
|
+
}
|
|
2517
|
+
isTransportConnected(transport) {
|
|
2518
|
+
return transport.connectionStatus !== octez_connect_types_1.TransportStatus.NOT_CONNECTED;
|
|
2519
|
+
}
|
|
1988
2520
|
/**
|
|
1989
2521
|
* Adds a requests to the "openRequests" set so we know what messages have already been answered/handled.
|
|
1990
2522
|
*
|
|
@@ -1993,7 +2525,64 @@ class DAppClient extends octez_connect_core_1.Client {
|
|
|
1993
2525
|
*/
|
|
1994
2526
|
addOpenRequest(id, promise) {
|
|
1995
2527
|
logger.log('addOpenRequest', this.name, `adding request ${id} and waiting for answer`);
|
|
2528
|
+
this.clearOpenRequestTimeout(id);
|
|
2529
|
+
this.openRequests.delete(id);
|
|
1996
2530
|
this.openRequests.set(id, promise);
|
|
2531
|
+
this.addOpenRequestTimeout(id, promise);
|
|
2532
|
+
}
|
|
2533
|
+
addOpenRequestTimeout(id, promise) {
|
|
2534
|
+
if (id === SESSION_UPDATE_REQUEST_ID) {
|
|
2535
|
+
return;
|
|
2536
|
+
}
|
|
2537
|
+
if (!Number.isFinite(this.requestTimeoutMs) || this.requestTimeoutMs <= 0) {
|
|
2538
|
+
return;
|
|
2539
|
+
}
|
|
2540
|
+
const timeout = setTimeout(() => {
|
|
2541
|
+
if (this.openRequests.get(id) !== promise) {
|
|
2542
|
+
return;
|
|
2543
|
+
}
|
|
2544
|
+
const errorResponse = {
|
|
2545
|
+
id,
|
|
2546
|
+
type: octez_connect_types_1.BeaconMessageType.Error,
|
|
2547
|
+
errorType: octez_connect_types_1.BeaconErrorType.PEER_UNREACHABLE,
|
|
2548
|
+
senderId: '',
|
|
2549
|
+
version: '2'
|
|
2550
|
+
};
|
|
2551
|
+
promise.reject(errorResponse);
|
|
2552
|
+
this.clearOpenRequest(id);
|
|
2553
|
+
this.events.emit(events_1.BeaconEvent.CHANNEL_CLOSED).catch((emitError) => {
|
|
2554
|
+
logger.error('addOpenRequestTimeout', emitError);
|
|
2555
|
+
});
|
|
2556
|
+
}, this.requestTimeoutMs);
|
|
2557
|
+
this.openRequestTimeouts.set(id, timeout);
|
|
2558
|
+
}
|
|
2559
|
+
clearOpenRequest(id) {
|
|
2560
|
+
this.clearOpenRequestTimeout(id);
|
|
2561
|
+
this.openRequests.delete(id);
|
|
2562
|
+
this.openRequestsOtherTabs.delete(id);
|
|
2563
|
+
}
|
|
2564
|
+
clearOpenRequestTimeout(id) {
|
|
2565
|
+
const timeout = this.openRequestTimeouts.get(id);
|
|
2566
|
+
if (timeout) {
|
|
2567
|
+
clearTimeout(timeout);
|
|
2568
|
+
this.openRequestTimeouts.delete(id);
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2571
|
+
clearAllOpenRequestTimeouts() {
|
|
2572
|
+
this.openRequestTimeouts.forEach((timeout) => clearTimeout(timeout));
|
|
2573
|
+
this.openRequestTimeouts.clear();
|
|
2574
|
+
}
|
|
2575
|
+
abortOpenRequests() {
|
|
2576
|
+
Array.from(this.openRequests.entries())
|
|
2577
|
+
.filter(([id]) => id !== SESSION_UPDATE_REQUEST_ID)
|
|
2578
|
+
.forEach(([id, promise]) => {
|
|
2579
|
+
promise.reject(this.createAbortedError(id));
|
|
2580
|
+
this.clearOpenRequest(id);
|
|
2581
|
+
});
|
|
2582
|
+
this.openRequests.clear();
|
|
2583
|
+
this.openRequestsOtherTabs.clear();
|
|
2584
|
+
this.acknowledgedRequests.clear();
|
|
2585
|
+
this.clearAllOpenRequestTimeouts();
|
|
1997
2586
|
}
|
|
1998
2587
|
sendNotificationWithAccessToken(notification) {
|
|
1999
2588
|
return __awaiter(this, void 0, void 0, function* () {
|