@solana-mobile/mobile-wallet-adapter-protocol 2.2.5 → 2.2.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/lib/esm/index.js CHANGED
@@ -12,8 +12,12 @@ const SolanaMobileWalletAdapterErrorCode = {
12
12
  ERROR_WALLET_NOT_FOUND: 'ERROR_WALLET_NOT_FOUND',
13
13
  ERROR_INVALID_PROTOCOL_VERSION: 'ERROR_INVALID_PROTOCOL_VERSION',
14
14
  ERROR_BROWSER_NOT_SUPPORTED: 'ERROR_BROWSER_NOT_SUPPORTED',
15
+ ERROR_LOOPBACK_ACCESS_BLOCKED: 'ERROR_LOOPBACK_ACCESS_BLOCKED',
16
+ ERROR_ASSOCIATION_CANCELLED: 'ERROR_ASSOCIATION_CANCELLED',
15
17
  };
16
18
  class SolanaMobileWalletAdapterError extends Error {
19
+ data;
20
+ code;
17
21
  constructor(...args) {
18
22
  const [code, message, data] = args;
19
23
  super(message);
@@ -33,6 +37,9 @@ const SolanaMobileWalletAdapterProtocolErrorCode = {
33
37
  ERROR_ATTEST_ORIGIN_ANDROID: -100,
34
38
  };
35
39
  class SolanaMobileWalletAdapterProtocolError extends Error {
40
+ data;
41
+ code;
42
+ jsonRpcMessageId;
36
43
  constructor(...args) {
37
44
  const [jsonRpcMessageId, code, message, data] = args;
38
45
  super(message);
@@ -43,31 +50,6 @@ class SolanaMobileWalletAdapterProtocolError extends Error {
43
50
  }
44
51
  }
45
52
 
46
- /******************************************************************************
47
- Copyright (c) Microsoft Corporation.
48
-
49
- Permission to use, copy, modify, and/or distribute this software for any
50
- purpose with or without fee is hereby granted.
51
-
52
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
53
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
54
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
55
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
56
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
57
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
58
- PERFORMANCE OF THIS SOFTWARE.
59
- ***************************************************************************** */
60
-
61
- function __awaiter(thisArg, _arguments, P, generator) {
62
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
63
- return new (P || (P = Promise))(function (resolve, reject) {
64
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
65
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
66
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
67
- step((generator = generator.apply(thisArg, _arguments || [])).next());
68
- });
69
- }
70
-
71
53
  function encode(input) {
72
54
  return window.btoa(input);
73
55
  }
@@ -89,15 +71,13 @@ function toUint8Array(base64EncodedByteArray) {
89
71
  .map((c) => c.charCodeAt(0)));
90
72
  }
91
73
 
92
- function createHelloReq(ecdhPublicKey, associationKeypairPrivateKey) {
93
- return __awaiter(this, void 0, void 0, function* () {
94
- const publicKeyBuffer = yield crypto.subtle.exportKey('raw', ecdhPublicKey);
95
- const signatureBuffer = yield crypto.subtle.sign({ hash: 'SHA-256', name: 'ECDSA' }, associationKeypairPrivateKey, publicKeyBuffer);
96
- const response = new Uint8Array(publicKeyBuffer.byteLength + signatureBuffer.byteLength);
97
- response.set(new Uint8Array(publicKeyBuffer), 0);
98
- response.set(new Uint8Array(signatureBuffer), publicKeyBuffer.byteLength);
99
- return response;
100
- });
74
+ async function createHelloReq(ecdhPublicKey, associationKeypairPrivateKey) {
75
+ const publicKeyBuffer = await crypto.subtle.exportKey('raw', ecdhPublicKey);
76
+ const signatureBuffer = await crypto.subtle.sign({ hash: 'SHA-256', name: 'ECDSA' }, associationKeypairPrivateKey, publicKeyBuffer);
77
+ const response = new Uint8Array(publicKeyBuffer.byteLength + signatureBuffer.byteLength);
78
+ response.set(new Uint8Array(publicKeyBuffer), 0);
79
+ response.set(new Uint8Array(signatureBuffer), publicKeyBuffer.byteLength);
80
+ return response;
101
81
  }
102
82
 
103
83
  function createSIWSMessage(payload) {
@@ -140,16 +120,14 @@ function createMobileWalletProxy(protocolVersion, protocolRequestHandler) {
140
120
  return null;
141
121
  }
142
122
  if (target[p] == null) {
143
- target[p] = function (inputParams) {
144
- return __awaiter(this, void 0, void 0, function* () {
145
- const { method, params } = handleMobileWalletRequest(p, inputParams, protocolVersion);
146
- const result = yield protocolRequestHandler(method, params);
147
- // if the request tried to sign in but the wallet did not return a sign in result, fallback on message signing
148
- if (method === 'authorize' && params.sign_in_payload && !result.sign_in_result) {
149
- result['sign_in_result'] = yield signInFallback(params.sign_in_payload, result, protocolRequestHandler);
150
- }
151
- return handleMobileWalletResponse(p, result, protocolVersion);
152
- });
123
+ target[p] = async function (inputParams) {
124
+ const { method, params } = handleMobileWalletRequest(p, inputParams, protocolVersion);
125
+ const result = await protocolRequestHandler(method, params);
126
+ // if the request tried to sign in but the wallet did not return a sign in result, fallback on message signing
127
+ if (method === 'authorize' && params.sign_in_payload && !result.sign_in_result) {
128
+ result['sign_in_result'] = await signInFallback(params.sign_in_payload, result, protocolRequestHandler);
129
+ }
130
+ return handleMobileWalletResponse(p, result, protocolVersion);
153
131
  };
154
132
  }
155
133
  return target[p];
@@ -253,39 +231,43 @@ function handleMobileWalletResponse(method, response, protocolVersion) {
253
231
  if (capabilities.supports_clone_authorization === true) {
254
232
  features.push(SolanaCloneAuthorization);
255
233
  }
256
- return Object.assign(Object.assign({}, capabilities), { features: features });
234
+ return {
235
+ ...capabilities,
236
+ features: features,
237
+ };
257
238
  }
258
239
  case 'v1': {
259
- return Object.assign(Object.assign({}, capabilities), { supports_sign_and_send_transactions: true, supports_clone_authorization: capabilities.features.includes(SolanaCloneAuthorization) });
240
+ return {
241
+ ...capabilities,
242
+ supports_sign_and_send_transactions: true,
243
+ supports_clone_authorization: capabilities.features.includes(SolanaCloneAuthorization)
244
+ };
260
245
  }
261
246
  }
262
247
  }
263
248
  }
264
249
  return response;
265
250
  }
266
- function signInFallback(signInPayload, authorizationResult, protocolRequestHandler) {
267
- var _a;
268
- return __awaiter(this, void 0, void 0, function* () {
269
- const domain = (_a = signInPayload.domain) !== null && _a !== void 0 ? _a : window.location.host;
270
- const address = authorizationResult.accounts[0].address;
271
- const siwsMessage = createSIWSMessageBase64Url(Object.assign(Object.assign({}, signInPayload), { domain, address: base64ToBase58(address) }));
272
- const signMessageResult = yield protocolRequestHandler('sign_messages', {
273
- addresses: [address],
274
- payloads: [siwsMessage]
275
- });
276
- const signedPayload = toUint8Array(signMessageResult.signed_payloads[0]);
277
- const signedMessage = fromUint8Array$1(signedPayload.slice(0, signedPayload.length - 64));
278
- const signature = fromUint8Array$1(signedPayload.slice(signedPayload.length - 64));
279
- const signInResult = {
280
- address: address,
281
- // Workaround: some wallets have been observed to only reply with the message signature.
282
- // This is non-compliant with the spec, but in the interest of maximizing compatibility,
283
- // detect this case and reuse the original message.
284
- signed_message: signedMessage.length == 0 ? siwsMessage : signedMessage,
285
- signature
286
- };
287
- return signInResult;
251
+ async function signInFallback(signInPayload, authorizationResult, protocolRequestHandler) {
252
+ const domain = signInPayload.domain ?? window.location.host;
253
+ const address = authorizationResult.accounts[0].address;
254
+ const siwsMessage = createSIWSMessageBase64Url({ ...signInPayload, domain, address: base64ToBase58(address) });
255
+ const signMessageResult = await protocolRequestHandler('sign_messages', {
256
+ addresses: [address],
257
+ payloads: [siwsMessage]
288
258
  });
259
+ const signedPayload = toUint8Array(signMessageResult.signed_payloads[0]);
260
+ const signedMessage = fromUint8Array$1(signedPayload.slice(0, signedPayload.length - 64));
261
+ const signature = fromUint8Array$1(signedPayload.slice(signedPayload.length - 64));
262
+ const signInResult = {
263
+ address: address,
264
+ // Workaround: some wallets have been observed to only reply with the message signature.
265
+ // This is non-compliant with the spec, but in the interest of maximizing compatibility,
266
+ // detect this case and reuse the original message.
267
+ signed_message: signedMessage.length == 0 ? siwsMessage : signedMessage,
268
+ signature
269
+ };
270
+ return signInResult;
289
271
  }
290
272
 
291
273
  const SEQUENCE_NUMBER_BYTES = 4;
@@ -301,28 +283,24 @@ function createSequenceNumberVector(sequenceNumber) {
301
283
 
302
284
  const INITIALIZATION_VECTOR_BYTES = 12;
303
285
  const ENCODED_PUBLIC_KEY_LENGTH_BYTES = 65;
304
- function encryptMessage(plaintext, sequenceNumber, sharedSecret) {
305
- return __awaiter(this, void 0, void 0, function* () {
306
- const sequenceNumberVector = createSequenceNumberVector(sequenceNumber);
307
- const initializationVector = new Uint8Array(INITIALIZATION_VECTOR_BYTES);
308
- crypto.getRandomValues(initializationVector);
309
- const ciphertext = yield crypto.subtle.encrypt(getAlgorithmParams(sequenceNumberVector, initializationVector), sharedSecret, new TextEncoder().encode(plaintext));
310
- const response = new Uint8Array(sequenceNumberVector.byteLength + initializationVector.byteLength + ciphertext.byteLength);
311
- response.set(new Uint8Array(sequenceNumberVector), 0);
312
- response.set(new Uint8Array(initializationVector), sequenceNumberVector.byteLength);
313
- response.set(new Uint8Array(ciphertext), sequenceNumberVector.byteLength + initializationVector.byteLength);
314
- return response;
315
- });
286
+ async function encryptMessage(plaintext, sequenceNumber, sharedSecret) {
287
+ const sequenceNumberVector = createSequenceNumberVector(sequenceNumber);
288
+ const initializationVector = new Uint8Array(INITIALIZATION_VECTOR_BYTES);
289
+ crypto.getRandomValues(initializationVector);
290
+ const ciphertext = await crypto.subtle.encrypt(getAlgorithmParams(sequenceNumberVector, initializationVector), sharedSecret, new TextEncoder().encode(plaintext));
291
+ const response = new Uint8Array(sequenceNumberVector.byteLength + initializationVector.byteLength + ciphertext.byteLength);
292
+ response.set(new Uint8Array(sequenceNumberVector), 0);
293
+ response.set(new Uint8Array(initializationVector), sequenceNumberVector.byteLength);
294
+ response.set(new Uint8Array(ciphertext), sequenceNumberVector.byteLength + initializationVector.byteLength);
295
+ return response;
316
296
  }
317
- function decryptMessage(message, sharedSecret) {
318
- return __awaiter(this, void 0, void 0, function* () {
319
- const sequenceNumberVector = message.slice(0, SEQUENCE_NUMBER_BYTES);
320
- const initializationVector = message.slice(SEQUENCE_NUMBER_BYTES, SEQUENCE_NUMBER_BYTES + INITIALIZATION_VECTOR_BYTES);
321
- const ciphertext = message.slice(SEQUENCE_NUMBER_BYTES + INITIALIZATION_VECTOR_BYTES);
322
- const plaintextBuffer = yield crypto.subtle.decrypt(getAlgorithmParams(sequenceNumberVector, initializationVector), sharedSecret, ciphertext);
323
- const plaintext = getUtf8Decoder().decode(plaintextBuffer);
324
- return plaintext;
325
- });
297
+ async function decryptMessage(message, sharedSecret) {
298
+ const sequenceNumberVector = message.slice(0, SEQUENCE_NUMBER_BYTES);
299
+ const initializationVector = message.slice(SEQUENCE_NUMBER_BYTES, SEQUENCE_NUMBER_BYTES + INITIALIZATION_VECTOR_BYTES);
300
+ const ciphertext = message.slice(SEQUENCE_NUMBER_BYTES + INITIALIZATION_VECTOR_BYTES);
301
+ const plaintextBuffer = await crypto.subtle.decrypt(getAlgorithmParams(sequenceNumberVector, initializationVector), sharedSecret, ciphertext);
302
+ const plaintext = getUtf8Decoder().decode(plaintextBuffer);
303
+ return plaintext;
326
304
  }
327
305
  function getAlgorithmParams(sequenceNumber, initializationVector) {
328
306
  return {
@@ -340,22 +318,18 @@ function getUtf8Decoder() {
340
318
  return _utf8Decoder;
341
319
  }
342
320
 
343
- function generateAssociationKeypair() {
344
- return __awaiter(this, void 0, void 0, function* () {
345
- return yield crypto.subtle.generateKey({
346
- name: 'ECDSA',
347
- namedCurve: 'P-256',
348
- }, false /* extractable */, ['sign'] /* keyUsages */);
349
- });
321
+ async function generateAssociationKeypair() {
322
+ return await crypto.subtle.generateKey({
323
+ name: 'ECDSA',
324
+ namedCurve: 'P-256',
325
+ }, false /* extractable */, ['sign'] /* keyUsages */);
350
326
  }
351
327
 
352
- function generateECDHKeypair() {
353
- return __awaiter(this, void 0, void 0, function* () {
354
- return yield crypto.subtle.generateKey({
355
- name: 'ECDH',
356
- namedCurve: 'P-256',
357
- }, false /* extractable */, ['deriveKey', 'deriveBits'] /* keyUsages */);
358
- });
328
+ async function generateECDHKeypair() {
329
+ return await crypto.subtle.generateKey({
330
+ name: 'ECDH',
331
+ namedCurve: 'P-256',
332
+ }, false /* extractable */, ['deriveKey', 'deriveBits'] /* keyUsages */);
359
333
  }
360
334
 
361
335
  // https://stackoverflow.com/a/9458996/802047
@@ -401,12 +375,12 @@ function getIntentURL(methodPathname, intentUrlBase) {
401
375
  try {
402
376
  baseUrl = new URL(intentUrlBase);
403
377
  }
404
- catch (_a) { } // eslint-disable-line no-empty
405
- if ((baseUrl === null || baseUrl === void 0 ? void 0 : baseUrl.protocol) !== 'https:') {
378
+ catch { } // eslint-disable-line no-empty
379
+ if (baseUrl?.protocol !== 'https:') {
406
380
  throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_FORBIDDEN_WALLET_BASE_URL, 'Base URLs supplied by wallets must be valid `https` URLs');
407
381
  }
408
382
  }
409
- baseUrl || (baseUrl = new URL(`${INTENT_NAME}:/`));
383
+ baseUrl ||= new URL(`${INTENT_NAME}:/`);
410
384
  const pathname = methodPathname.startsWith('/')
411
385
  ? // Method is an absolute path. Replace it wholesale.
412
386
  methodPathname
@@ -414,94 +388,82 @@ function getIntentURL(methodPathname, intentUrlBase) {
414
388
  [...getPathParts(baseUrl.pathname), ...getPathParts(methodPathname)].join('/');
415
389
  return new URL(pathname, baseUrl);
416
390
  }
417
- function getAssociateAndroidIntentURL(associationPublicKey, putativePort, associationURLBase, protocolVersions = ['v1']) {
418
- return __awaiter(this, void 0, void 0, function* () {
419
- const associationPort = assertAssociationPort(putativePort);
420
- const exportedKey = yield crypto.subtle.exportKey('raw', associationPublicKey);
421
- const encodedKey = arrayBufferToBase64String(exportedKey);
422
- const url = getIntentURL('v1/associate/local', associationURLBase);
423
- url.searchParams.set('association', getStringWithURLUnsafeCharactersReplaced(encodedKey));
424
- url.searchParams.set('port', `${associationPort}`);
425
- protocolVersions.forEach((version) => {
426
- url.searchParams.set('v', version);
427
- });
428
- return url;
391
+ async function getAssociateAndroidIntentURL(associationPublicKey, putativePort, associationURLBase, protocolVersions = ['v1']) {
392
+ const associationPort = assertAssociationPort(putativePort);
393
+ const exportedKey = await crypto.subtle.exportKey('raw', associationPublicKey);
394
+ const encodedKey = arrayBufferToBase64String(exportedKey);
395
+ const url = getIntentURL('v1/associate/local', associationURLBase);
396
+ url.searchParams.set('association', getStringWithURLUnsafeCharactersReplaced(encodedKey));
397
+ url.searchParams.set('port', `${associationPort}`);
398
+ protocolVersions.forEach((version) => {
399
+ url.searchParams.set('v', version);
429
400
  });
401
+ return url;
430
402
  }
431
- function getRemoteAssociateAndroidIntentURL(associationPublicKey, hostAuthority, reflectorId, associationURLBase, protocolVersions = ['v1']) {
432
- return __awaiter(this, void 0, void 0, function* () {
433
- const exportedKey = yield crypto.subtle.exportKey('raw', associationPublicKey);
434
- const encodedKey = arrayBufferToBase64String(exportedKey);
435
- const url = getIntentURL('v1/associate/remote', associationURLBase);
436
- url.searchParams.set('association', getStringWithURLUnsafeCharactersReplaced(encodedKey));
437
- url.searchParams.set('reflector', `${hostAuthority}`);
438
- url.searchParams.set('id', `${fromUint8Array$1(reflectorId, true)}`);
439
- protocolVersions.forEach((version) => {
440
- url.searchParams.set('v', version);
441
- });
442
- return url;
403
+ async function getRemoteAssociateAndroidIntentURL(associationPublicKey, hostAuthority, reflectorId, associationURLBase, protocolVersions = ['v1']) {
404
+ const exportedKey = await crypto.subtle.exportKey('raw', associationPublicKey);
405
+ const encodedKey = arrayBufferToBase64String(exportedKey);
406
+ const url = getIntentURL('v1/associate/remote', associationURLBase);
407
+ url.searchParams.set('association', getStringWithURLUnsafeCharactersReplaced(encodedKey));
408
+ url.searchParams.set('reflector', `${hostAuthority}`);
409
+ url.searchParams.set('id', `${fromUint8Array$1(reflectorId, true)}`);
410
+ protocolVersions.forEach((version) => {
411
+ url.searchParams.set('v', version);
443
412
  });
413
+ return url;
444
414
  }
445
415
 
446
- function encryptJsonRpcMessage(jsonRpcMessage, sharedSecret) {
447
- return __awaiter(this, void 0, void 0, function* () {
448
- const plaintext = JSON.stringify(jsonRpcMessage);
449
- const sequenceNumber = jsonRpcMessage.id;
450
- return encryptMessage(plaintext, sequenceNumber, sharedSecret);
451
- });
416
+ async function encryptJsonRpcMessage(jsonRpcMessage, sharedSecret) {
417
+ const plaintext = JSON.stringify(jsonRpcMessage);
418
+ const sequenceNumber = jsonRpcMessage.id;
419
+ return encryptMessage(plaintext, sequenceNumber, sharedSecret);
452
420
  }
453
- function decryptJsonRpcMessage(message, sharedSecret) {
454
- return __awaiter(this, void 0, void 0, function* () {
455
- const plaintext = yield decryptMessage(message, sharedSecret);
456
- const jsonRpcMessage = JSON.parse(plaintext);
457
- if (Object.hasOwnProperty.call(jsonRpcMessage, 'error')) {
458
- throw new SolanaMobileWalletAdapterProtocolError(jsonRpcMessage.id, jsonRpcMessage.error.code, jsonRpcMessage.error.message);
459
- }
460
- return jsonRpcMessage;
461
- });
421
+ async function decryptJsonRpcMessage(message, sharedSecret) {
422
+ const plaintext = await decryptMessage(message, sharedSecret);
423
+ const jsonRpcMessage = JSON.parse(plaintext);
424
+ if (Object.hasOwnProperty.call(jsonRpcMessage, 'error')) {
425
+ throw new SolanaMobileWalletAdapterProtocolError(jsonRpcMessage.id, jsonRpcMessage.error.code, jsonRpcMessage.error.message);
426
+ }
427
+ return jsonRpcMessage;
462
428
  }
463
429
 
464
- function parseHelloRsp(payloadBuffer, // The X9.62-encoded wallet endpoint ephemeral ECDH public keypoint.
430
+ async function parseHelloRsp(payloadBuffer, // The X9.62-encoded wallet endpoint ephemeral ECDH public keypoint.
465
431
  associationPublicKey, ecdhPrivateKey) {
466
- return __awaiter(this, void 0, void 0, function* () {
467
- const [associationPublicKeyBuffer, walletPublicKey] = yield Promise.all([
468
- crypto.subtle.exportKey('raw', associationPublicKey),
469
- crypto.subtle.importKey('raw', payloadBuffer.slice(0, ENCODED_PUBLIC_KEY_LENGTH_BYTES), { name: 'ECDH', namedCurve: 'P-256' }, false /* extractable */, [] /* keyUsages */),
470
- ]);
471
- const sharedSecret = yield crypto.subtle.deriveBits({ name: 'ECDH', public: walletPublicKey }, ecdhPrivateKey, 256);
472
- const ecdhSecretKey = yield crypto.subtle.importKey('raw', sharedSecret, 'HKDF', false /* extractable */, ['deriveKey'] /* keyUsages */);
473
- const aesKeyMaterialVal = yield crypto.subtle.deriveKey({
474
- name: 'HKDF',
475
- hash: 'SHA-256',
476
- salt: new Uint8Array(associationPublicKeyBuffer),
477
- info: new Uint8Array(),
478
- }, ecdhSecretKey, { name: 'AES-GCM', length: 128 }, false /* extractable */, ['encrypt', 'decrypt']);
479
- return aesKeyMaterialVal;
480
- });
432
+ const [associationPublicKeyBuffer, walletPublicKey] = await Promise.all([
433
+ crypto.subtle.exportKey('raw', associationPublicKey),
434
+ crypto.subtle.importKey('raw', payloadBuffer.slice(0, ENCODED_PUBLIC_KEY_LENGTH_BYTES), { name: 'ECDH', namedCurve: 'P-256' }, false /* extractable */, [] /* keyUsages */),
435
+ ]);
436
+ const sharedSecret = await crypto.subtle.deriveBits({ name: 'ECDH', public: walletPublicKey }, ecdhPrivateKey, 256);
437
+ const ecdhSecretKey = await crypto.subtle.importKey('raw', sharedSecret, 'HKDF', false /* extractable */, ['deriveKey'] /* keyUsages */);
438
+ const aesKeyMaterialVal = await crypto.subtle.deriveKey({
439
+ name: 'HKDF',
440
+ hash: 'SHA-256',
441
+ salt: new Uint8Array(associationPublicKeyBuffer),
442
+ info: new Uint8Array(),
443
+ }, ecdhSecretKey, { name: 'AES-GCM', length: 128 }, false /* extractable */, ['encrypt', 'decrypt']);
444
+ return aesKeyMaterialVal;
481
445
  }
482
446
 
483
- function parseSessionProps(message, sharedSecret) {
484
- return __awaiter(this, void 0, void 0, function* () {
485
- const plaintext = yield decryptMessage(message, sharedSecret);
486
- const jsonProperties = JSON.parse(plaintext);
487
- let protocolVersion = 'legacy';
488
- if (Object.hasOwnProperty.call(jsonProperties, 'v')) {
489
- switch (jsonProperties.v) {
490
- case 1:
491
- case '1':
492
- case 'v1':
493
- protocolVersion = 'v1';
494
- break;
495
- case 'legacy':
496
- protocolVersion = 'legacy';
497
- break;
498
- default:
499
- throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_INVALID_PROTOCOL_VERSION, `Unknown/unsupported protocol version: ${jsonProperties.v}`);
500
- }
447
+ async function parseSessionProps(message, sharedSecret) {
448
+ const plaintext = await decryptMessage(message, sharedSecret);
449
+ const jsonProperties = JSON.parse(plaintext);
450
+ let protocolVersion = 'legacy';
451
+ if (Object.hasOwnProperty.call(jsonProperties, 'v')) {
452
+ switch (jsonProperties.v) {
453
+ case 1:
454
+ case '1':
455
+ case 'v1':
456
+ protocolVersion = 'v1';
457
+ break;
458
+ case 'legacy':
459
+ protocolVersion = 'legacy';
460
+ break;
461
+ default:
462
+ throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_INVALID_PROTOCOL_VERSION, `Unknown/unsupported protocol version: ${jsonProperties.v}`);
501
463
  }
502
- return ({
503
- protocol_version: protocolVersion
504
- });
464
+ }
465
+ return ({
466
+ protocol_version: protocolVersion
505
467
  });
506
468
  }
507
469
 
@@ -546,47 +508,43 @@ function launchUrlThroughHiddenFrame(url) {
546
508
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
547
509
  _frame.contentWindow.location.href = url.toString();
548
510
  }
549
- function launchAssociation(associationUrl) {
550
- return __awaiter(this, void 0, void 0, function* () {
551
- if (associationUrl.protocol === 'https:') {
552
- // The association URL is an Android 'App Link' or iOS 'Universal Link'.
553
- // These are regular web URLs that are designed to launch an app if it
554
- // is installed or load the actual target webpage if not.
555
- window.location.assign(associationUrl);
556
- }
557
- else {
558
- // The association URL has a custom protocol (eg. `solana-wallet:`)
559
- try {
560
- const browser = getBrowser();
561
- switch (browser) {
562
- case Browser.Firefox:
563
- // If a custom protocol is not supported in Firefox, it throws.
564
- launchUrlThroughHiddenFrame(associationUrl);
565
- // If we reached this line, it's supported.
566
- break;
567
- case Browser.Other: {
568
- const detectionPromise = getDetectionPromise();
569
- window.location.assign(associationUrl);
570
- yield detectionPromise;
571
- break;
572
- }
573
- default:
574
- assertUnreachable(browser);
511
+ async function launchAssociation(associationUrl) {
512
+ if (associationUrl.protocol === 'https:') {
513
+ // The association URL is an Android 'App Link' or iOS 'Universal Link'.
514
+ // These are regular web URLs that are designed to launch an app if it
515
+ // is installed or load the actual target webpage if not.
516
+ window.location.assign(associationUrl);
517
+ }
518
+ else {
519
+ // The association URL has a custom protocol (eg. `solana-wallet:`)
520
+ try {
521
+ const browser = getBrowser();
522
+ switch (browser) {
523
+ case Browser.Firefox:
524
+ // If a custom protocol is not supported in Firefox, it throws.
525
+ launchUrlThroughHiddenFrame(associationUrl);
526
+ // If we reached this line, it's supported.
527
+ break;
528
+ case Browser.Other: {
529
+ const detectionPromise = getDetectionPromise();
530
+ window.location.assign(associationUrl);
531
+ await detectionPromise;
532
+ break;
575
533
  }
576
- }
577
- catch (e) {
578
- throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_WALLET_NOT_FOUND, 'Found no installed wallet that supports the mobile wallet protocol.');
534
+ default:
535
+ assertUnreachable(browser);
579
536
  }
580
537
  }
581
- });
538
+ catch (e) {
539
+ throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_WALLET_NOT_FOUND, 'Found no installed wallet that supports the mobile wallet protocol.');
540
+ }
541
+ }
582
542
  }
583
- function startSession(associationPublicKey, associationURLBase) {
584
- return __awaiter(this, void 0, void 0, function* () {
585
- const randomAssociationPort = getRandomAssociationPort();
586
- const associationUrl = yield getAssociateAndroidIntentURL(associationPublicKey, randomAssociationPort, associationURLBase);
587
- yield launchAssociation(associationUrl);
588
- return randomAssociationPort;
589
- });
543
+ async function startSession(associationPublicKey, associationURLBase) {
544
+ const randomAssociationPort = getRandomAssociationPort();
545
+ const associationUrl = await getAssociateAndroidIntentURL(associationPublicKey, randomAssociationPort, associationURLBase);
546
+ await launchAssociation(associationUrl);
547
+ return randomAssociationPort;
590
548
  }
591
549
 
592
550
  const WEBSOCKET_CONNECTION_CONFIG = {
@@ -615,7 +573,7 @@ function assertSecureEndpointSpecificURI(walletUriBase) {
615
573
  try {
616
574
  url = new URL(walletUriBase);
617
575
  }
618
- catch (_a) {
576
+ catch {
619
577
  throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_FORBIDDEN_WALLET_BASE_URL, 'Invalid base URL supplied by wallet');
620
578
  }
621
579
  if (url.protocol !== 'https:') {
@@ -640,25 +598,38 @@ function getReflectorIdFromByteArray(byteArray) {
640
598
  let { value: length, offset } = decodeVarLong(byteArray);
641
599
  return new Uint8Array(byteArray.slice(offset, offset + length));
642
600
  }
643
- function transact(callback, config) {
644
- return __awaiter(this, void 0, void 0, function* () {
645
- assertSecureContext();
646
- const associationKeypair = yield generateAssociationKeypair();
647
- const sessionPort = yield startSession(associationKeypair.publicKey, config === null || config === void 0 ? void 0 : config.baseUri);
648
- const websocketURL = `ws://localhost:${sessionPort}/solana-wallet`;
649
- let connectionStartTime;
650
- const getNextRetryDelayMs = (() => {
651
- const schedule = [...WEBSOCKET_CONNECTION_CONFIG.retryDelayScheduleMs];
652
- return () => (schedule.length > 1 ? schedule.shift() : schedule[0]);
653
- })();
654
- let nextJsonRpcMessageId = 1;
655
- let lastKnownInboundSequenceNumber = 0;
656
- let state = { __type: 'disconnected' };
657
- return new Promise((resolve, reject) => {
658
- let socket;
601
+ async function transact(callback, config) {
602
+ const { wallet, close } = await startScenario(config);
603
+ try {
604
+ return await callback(await wallet);
605
+ }
606
+ finally {
607
+ close();
608
+ }
609
+ }
610
+ async function startScenario(config) {
611
+ assertSecureContext();
612
+ const associationKeypair = await generateAssociationKeypair();
613
+ const sessionPort = await startSession(associationKeypair.publicKey, config?.baseUri);
614
+ const websocketURL = `ws://localhost:${sessionPort}/solana-wallet`;
615
+ let connectionStartTime;
616
+ const getNextRetryDelayMs = (() => {
617
+ const schedule = [...WEBSOCKET_CONNECTION_CONFIG.retryDelayScheduleMs];
618
+ return () => (schedule.length > 1 ? schedule.shift() : schedule[0]);
619
+ })();
620
+ let nextJsonRpcMessageId = 1;
621
+ let lastKnownInboundSequenceNumber = 0;
622
+ let state = { __type: 'disconnected' };
623
+ let socket;
624
+ let sessionEstablished = false;
625
+ let handleForceClose;
626
+ return { close: () => {
627
+ socket.close();
628
+ handleForceClose();
629
+ }, wallet: new Promise((resolve, reject) => {
659
630
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
660
631
  const jsonRpcResponsePromises = {};
661
- const handleOpen = () => __awaiter(this, void 0, void 0, function* () {
632
+ const handleOpen = async () => {
662
633
  if (state.__type !== 'connecting') {
663
634
  console.warn('Expected adapter state to be `connecting` at the moment the websocket opens. ' +
664
635
  `Got \`${state.__type}\`.`);
@@ -672,14 +643,14 @@ function transact(callback, config) {
672
643
  // APP_PING was sent by the wallet/websocket server. We must continue to support this behavior
673
644
  // in case the user is using a wallet that has not updated their walletlib implementation.
674
645
  const { associationKeypair } = state;
675
- const ecdhKeypair = yield generateECDHKeypair();
676
- socket.send(yield createHelloReq(ecdhKeypair.publicKey, associationKeypair.privateKey));
646
+ const ecdhKeypair = await generateECDHKeypair();
647
+ socket.send(await createHelloReq(ecdhKeypair.publicKey, associationKeypair.privateKey));
677
648
  state = {
678
649
  __type: 'hello_req_sent',
679
650
  associationPublicKey: associationKeypair.publicKey,
680
651
  ecdhPrivateKey: ecdhKeypair.privateKey,
681
652
  };
682
- });
653
+ };
683
654
  const handleClose = (evt) => {
684
655
  if (evt.wasClean) {
685
656
  state = { __type: 'disconnected' };
@@ -689,28 +660,28 @@ function transact(callback, config) {
689
660
  }
690
661
  disposeSocket();
691
662
  };
692
- const handleError = (_evt) => __awaiter(this, void 0, void 0, function* () {
663
+ const handleError = async (_evt) => {
693
664
  disposeSocket();
694
665
  if (Date.now() - connectionStartTime >= WEBSOCKET_CONNECTION_CONFIG.timeoutMs) {
695
666
  reject(new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_SESSION_TIMEOUT, `Failed to connect to the wallet websocket at ${websocketURL}.`));
696
667
  }
697
668
  else {
698
- yield new Promise((resolve) => {
669
+ await new Promise((resolve) => {
699
670
  const retryDelayMs = getNextRetryDelayMs();
700
671
  retryWaitTimeoutId = window.setTimeout(resolve, retryDelayMs);
701
672
  });
702
673
  attemptSocketConnection();
703
674
  }
704
- });
705
- const handleMessage = (evt) => __awaiter(this, void 0, void 0, function* () {
706
- const responseBuffer = yield evt.data.arrayBuffer();
675
+ };
676
+ const handleMessage = async (evt) => {
677
+ const responseBuffer = await evt.data.arrayBuffer();
707
678
  switch (state.__type) {
708
679
  case 'connecting':
709
680
  if (responseBuffer.byteLength !== 0) {
710
681
  throw new Error('Encountered unexpected message while connecting');
711
682
  }
712
- const ecdhKeypair = yield generateECDHKeypair();
713
- socket.send(yield createHelloReq(ecdhKeypair.publicKey, associationKeypair.privateKey));
683
+ const ecdhKeypair = await generateECDHKeypair();
684
+ socket.send(await createHelloReq(ecdhKeypair.publicKey, associationKeypair.privateKey));
714
685
  state = {
715
686
  __type: 'hello_req_sent',
716
687
  associationPublicKey: associationKeypair.publicKey,
@@ -725,7 +696,7 @@ function transact(callback, config) {
725
696
  throw new Error('Encrypted message has invalid sequence number');
726
697
  }
727
698
  lastKnownInboundSequenceNumber = sequenceNumber;
728
- const jsonRpcMessage = yield decryptJsonRpcMessage(responseBuffer, state.sharedSecret);
699
+ const jsonRpcMessage = await decryptJsonRpcMessage(responseBuffer, state.sharedSecret);
729
700
  const responsePromise = jsonRpcResponsePromises[jsonRpcMessage.id];
730
701
  delete jsonRpcResponsePromises[jsonRpcMessage.id];
731
702
  responsePromise.resolve(jsonRpcMessage.result);
@@ -744,8 +715,8 @@ function transact(callback, config) {
744
715
  case 'hello_req_sent': {
745
716
  // if we receive an APP_PING message (empty message), resend the HELLO_REQ (see above)
746
717
  if (responseBuffer.byteLength === 0) {
747
- const ecdhKeypair = yield generateECDHKeypair();
748
- socket.send(yield createHelloReq(ecdhKeypair.publicKey, associationKeypair.privateKey));
718
+ const ecdhKeypair = await generateECDHKeypair();
719
+ socket.send(await createHelloReq(ecdhKeypair.publicKey, associationKeypair.privateKey));
749
720
  state = {
750
721
  __type: 'hello_req_sent',
751
722
  associationPublicKey: associationKeypair.publicKey,
@@ -753,10 +724,10 @@ function transact(callback, config) {
753
724
  };
754
725
  break;
755
726
  }
756
- const sharedSecret = yield parseHelloRsp(responseBuffer, state.associationPublicKey, state.ecdhPrivateKey);
727
+ const sharedSecret = await parseHelloRsp(responseBuffer, state.associationPublicKey, state.ecdhPrivateKey);
757
728
  const sessionPropertiesBuffer = responseBuffer.slice(ENCODED_PUBLIC_KEY_LENGTH_BYTES);
758
729
  const sessionProperties = sessionPropertiesBuffer.byteLength !== 0
759
- ? yield (() => __awaiter(this, void 0, void 0, function* () {
730
+ ? await (async () => {
760
731
  const sequenceNumberVector = sessionPropertiesBuffer.slice(0, SEQUENCE_NUMBER_BYTES);
761
732
  const sequenceNumber = getSequenceNumberFromByteArray(sequenceNumberVector);
762
733
  if (sequenceNumber !== (lastKnownInboundSequenceNumber + 1)) {
@@ -764,15 +735,15 @@ function transact(callback, config) {
764
735
  }
765
736
  lastKnownInboundSequenceNumber = sequenceNumber;
766
737
  return parseSessionProps(sessionPropertiesBuffer, sharedSecret);
767
- }))() : { protocol_version: 'legacy' };
738
+ })() : { protocol_version: 'legacy' };
768
739
  state = { __type: 'connected', sharedSecret, sessionProperties };
769
- const wallet = createMobileWalletProxy(sessionProperties.protocol_version, (method, params) => __awaiter(this, void 0, void 0, function* () {
740
+ const wallet = createMobileWalletProxy(sessionProperties.protocol_version, async (method, params) => {
770
741
  const id = nextJsonRpcMessageId++;
771
- socket.send(yield encryptJsonRpcMessage({
742
+ socket.send(await encryptJsonRpcMessage({
772
743
  id,
773
744
  jsonrpc: '2.0',
774
745
  method,
775
- params: params !== null && params !== void 0 ? params : {},
746
+ params: params ?? {},
776
747
  }, sharedSecret));
777
748
  return new Promise((resolve, reject) => {
778
749
  jsonRpcResponsePromises[id] = {
@@ -798,21 +769,25 @@ function transact(callback, config) {
798
769
  reject,
799
770
  };
800
771
  });
801
- }));
772
+ });
773
+ sessionEstablished = true;
802
774
  try {
803
- resolve(yield callback(wallet));
775
+ resolve(wallet);
804
776
  }
805
777
  catch (e) {
806
778
  reject(e);
807
779
  }
808
- finally {
809
- disposeSocket();
810
- socket.close();
811
- }
812
780
  break;
813
781
  }
814
782
  }
815
- });
783
+ };
784
+ handleForceClose = () => {
785
+ socket.removeEventListener('message', handleMessage);
786
+ disposeSocket();
787
+ if (!sessionEstablished) {
788
+ reject(new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_SESSION_CLOSED, `The wallet session was closed before connection.`, { closeEvent: new CloseEvent('socket was closed before connection') }));
789
+ }
790
+ };
816
791
  let disposeSocket;
817
792
  let retryWaitTimeoutId;
818
793
  const attemptSocketConnection = () => {
@@ -837,244 +812,241 @@ function transact(callback, config) {
837
812
  };
838
813
  };
839
814
  attemptSocketConnection();
840
- });
841
- });
815
+ }) };
842
816
  }
843
- function startRemoteScenario(config) {
844
- return __awaiter(this, void 0, void 0, function* () {
845
- assertSecureContext();
846
- const associationKeypair = yield generateAssociationKeypair();
847
- const websocketURL = `wss://${config === null || config === void 0 ? void 0 : config.remoteHostAuthority}/reflect`;
848
- let connectionStartTime;
849
- const getNextRetryDelayMs = (() => {
850
- const schedule = [...WEBSOCKET_CONNECTION_CONFIG.retryDelayScheduleMs];
851
- return () => (schedule.length > 1 ? schedule.shift() : schedule[0]);
852
- })();
853
- let nextJsonRpcMessageId = 1;
854
- let lastKnownInboundSequenceNumber = 0;
855
- let encoding;
856
- let state = { __type: 'disconnected' };
857
- let socket;
858
- let disposeSocket;
859
- let decodeBytes = (evt) => __awaiter(this, void 0, void 0, function* () {
860
- if (encoding == 'base64') { // base64 encoding
861
- const message = yield evt.data;
862
- return toUint8Array(message).buffer;
817
+ async function startRemoteScenario(config) {
818
+ assertSecureContext();
819
+ const associationKeypair = await generateAssociationKeypair();
820
+ const websocketURL = `wss://${config?.remoteHostAuthority}/reflect`;
821
+ let connectionStartTime;
822
+ const getNextRetryDelayMs = (() => {
823
+ const schedule = [...WEBSOCKET_CONNECTION_CONFIG.retryDelayScheduleMs];
824
+ return () => (schedule.length > 1 ? schedule.shift() : schedule[0]);
825
+ })();
826
+ let nextJsonRpcMessageId = 1;
827
+ let lastKnownInboundSequenceNumber = 0;
828
+ let encoding;
829
+ let state = { __type: 'disconnected' };
830
+ let socket;
831
+ let disposeSocket;
832
+ let decodeBytes = async (evt) => {
833
+ if (encoding == 'base64') { // base64 encoding
834
+ const message = await evt.data;
835
+ return toUint8Array(message).buffer;
836
+ }
837
+ else {
838
+ return await evt.data.arrayBuffer();
839
+ }
840
+ };
841
+ // Reflector Connection Phase
842
+ // here we connect to the reflector and wait for the REFLECTOR_ID message
843
+ // so we build the association URL and return that back to the caller
844
+ const associationUrl = await new Promise((resolve, reject) => {
845
+ const handleOpen = async () => {
846
+ if (state.__type !== 'connecting') {
847
+ console.warn('Expected adapter state to be `connecting` at the moment the websocket opens. ' +
848
+ `Got \`${state.__type}\`.`);
849
+ return;
850
+ }
851
+ if (socket.protocol.includes(WEBSOCKET_PROTOCOL_BASE64)) {
852
+ encoding = 'base64';
863
853
  }
864
854
  else {
865
- return yield evt.data.arrayBuffer();
855
+ encoding = 'binary';
866
856
  }
867
- });
868
- // Reflector Connection Phase
869
- // here we connect to the reflector and wait for the REFLECTOR_ID message
870
- // so we build the association URL and return that back to the caller
871
- const associationUrl = yield new Promise((resolve, reject) => {
872
- const handleOpen = () => __awaiter(this, void 0, void 0, function* () {
873
- if (state.__type !== 'connecting') {
874
- console.warn('Expected adapter state to be `connecting` at the moment the websocket opens. ' +
875
- `Got \`${state.__type}\`.`);
876
- return;
877
- }
878
- if (socket.protocol.includes(WEBSOCKET_PROTOCOL_BASE64)) {
879
- encoding = 'base64';
880
- }
881
- else {
882
- encoding = 'binary';
883
- }
884
- socket.removeEventListener('open', handleOpen);
885
- });
886
- const handleClose = (evt) => {
887
- if (evt.wasClean) {
888
- state = { __type: 'disconnected' };
889
- }
890
- else {
891
- reject(new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_SESSION_CLOSED, `The wallet session dropped unexpectedly (${evt.code}: ${evt.reason}).`, { closeEvent: evt }));
892
- }
893
- disposeSocket();
894
- };
895
- const handleError = (_evt) => __awaiter(this, void 0, void 0, function* () {
896
- disposeSocket();
897
- if (Date.now() - connectionStartTime >= WEBSOCKET_CONNECTION_CONFIG.timeoutMs) {
898
- reject(new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_SESSION_TIMEOUT, `Failed to connect to the wallet websocket at ${websocketURL}.`));
899
- }
900
- else {
901
- yield new Promise((resolve) => {
902
- const retryDelayMs = getNextRetryDelayMs();
903
- retryWaitTimeoutId = window.setTimeout(resolve, retryDelayMs);
904
- });
905
- attemptSocketConnection();
906
- }
907
- });
908
- const handleReflectorIdMessage = (evt) => __awaiter(this, void 0, void 0, function* () {
909
- const responseBuffer = yield decodeBytes(evt);
910
- if (state.__type === 'connecting') {
911
- if (responseBuffer.byteLength == 0) {
912
- throw new Error('Encountered unexpected message while connecting');
913
- }
914
- const reflectorId = getReflectorIdFromByteArray(responseBuffer);
915
- state = {
916
- __type: 'reflector_id_received',
917
- reflectorId: reflectorId
918
- };
919
- const associationUrl = yield getRemoteAssociateAndroidIntentURL(associationKeypair.publicKey, config.remoteHostAuthority, reflectorId, config === null || config === void 0 ? void 0 : config.baseUri);
920
- socket.removeEventListener('message', handleReflectorIdMessage);
921
- resolve(associationUrl);
922
- }
923
- });
924
- let retryWaitTimeoutId;
925
- const attemptSocketConnection = () => {
926
- if (disposeSocket) {
927
- disposeSocket();
928
- }
929
- state = { __type: 'connecting', associationKeypair };
930
- if (connectionStartTime === undefined) {
931
- connectionStartTime = Date.now();
857
+ socket.removeEventListener('open', handleOpen);
858
+ };
859
+ const handleClose = (evt) => {
860
+ if (evt.wasClean) {
861
+ state = { __type: 'disconnected' };
862
+ }
863
+ else {
864
+ reject(new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_SESSION_CLOSED, `The wallet session dropped unexpectedly (${evt.code}: ${evt.reason}).`, { closeEvent: evt }));
865
+ }
866
+ disposeSocket();
867
+ };
868
+ const handleError = async (_evt) => {
869
+ disposeSocket();
870
+ if (Date.now() - connectionStartTime >= WEBSOCKET_CONNECTION_CONFIG.timeoutMs) {
871
+ reject(new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_SESSION_TIMEOUT, `Failed to connect to the wallet websocket at ${websocketURL}.`));
872
+ }
873
+ else {
874
+ await new Promise((resolve) => {
875
+ const retryDelayMs = getNextRetryDelayMs();
876
+ retryWaitTimeoutId = window.setTimeout(resolve, retryDelayMs);
877
+ });
878
+ attemptSocketConnection();
879
+ }
880
+ };
881
+ const handleReflectorIdMessage = async (evt) => {
882
+ const responseBuffer = await decodeBytes(evt);
883
+ if (state.__type === 'connecting') {
884
+ if (responseBuffer.byteLength == 0) {
885
+ throw new Error('Encountered unexpected message while connecting');
932
886
  }
933
- socket = new WebSocket(websocketURL, [WEBSOCKET_PROTOCOL_BINARY, WEBSOCKET_PROTOCOL_BASE64]);
934
- socket.addEventListener('open', handleOpen);
935
- socket.addEventListener('close', handleClose);
936
- socket.addEventListener('error', handleError);
937
- socket.addEventListener('message', handleReflectorIdMessage);
938
- disposeSocket = () => {
939
- window.clearTimeout(retryWaitTimeoutId);
940
- socket.removeEventListener('open', handleOpen);
941
- socket.removeEventListener('close', handleClose);
942
- socket.removeEventListener('error', handleError);
943
- socket.removeEventListener('message', handleReflectorIdMessage);
887
+ const reflectorId = getReflectorIdFromByteArray(responseBuffer);
888
+ state = {
889
+ __type: 'reflector_id_received',
890
+ reflectorId: reflectorId
944
891
  };
892
+ const associationUrl = await getRemoteAssociateAndroidIntentURL(associationKeypair.publicKey, config.remoteHostAuthority, reflectorId, config?.baseUri);
893
+ socket.removeEventListener('message', handleReflectorIdMessage);
894
+ resolve(associationUrl);
895
+ }
896
+ };
897
+ let retryWaitTimeoutId;
898
+ const attemptSocketConnection = () => {
899
+ if (disposeSocket) {
900
+ disposeSocket();
901
+ }
902
+ state = { __type: 'connecting', associationKeypair };
903
+ if (connectionStartTime === undefined) {
904
+ connectionStartTime = Date.now();
905
+ }
906
+ socket = new WebSocket(websocketURL, [WEBSOCKET_PROTOCOL_BINARY, WEBSOCKET_PROTOCOL_BASE64]);
907
+ socket.addEventListener('open', handleOpen);
908
+ socket.addEventListener('close', handleClose);
909
+ socket.addEventListener('error', handleError);
910
+ socket.addEventListener('message', handleReflectorIdMessage);
911
+ disposeSocket = () => {
912
+ window.clearTimeout(retryWaitTimeoutId);
913
+ socket.removeEventListener('open', handleOpen);
914
+ socket.removeEventListener('close', handleClose);
915
+ socket.removeEventListener('error', handleError);
916
+ socket.removeEventListener('message', handleReflectorIdMessage);
945
917
  };
946
- attemptSocketConnection();
947
- });
948
- // Wallet Connection Phase
949
- // here we return the association URL (containing the reflector ID) to the caller +
950
- // a promise that will resolve the MobileWallet object once the wallet connects.
951
- let sessionEstablished = false;
952
- let handleClose;
953
- return { associationUrl, close: () => {
954
- socket.close();
955
- handleClose();
956
- }, wallet: new Promise((resolve, reject) => {
957
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
958
- const jsonRpcResponsePromises = {};
959
- const handleMessage = (evt) => __awaiter(this, void 0, void 0, function* () {
960
- const responseBuffer = yield decodeBytes(evt);
961
- switch (state.__type) {
962
- case 'reflector_id_received':
963
- if (responseBuffer.byteLength !== 0) {
964
- throw new Error('Encountered unexpected message while awaiting reflection');
918
+ };
919
+ attemptSocketConnection();
920
+ });
921
+ // Wallet Connection Phase
922
+ // here we return the association URL (containing the reflector ID) to the caller +
923
+ // a promise that will resolve the MobileWallet object once the wallet connects.
924
+ let sessionEstablished = false;
925
+ let handleClose;
926
+ return { associationUrl, close: () => {
927
+ socket.close();
928
+ handleClose();
929
+ }, wallet: new Promise((resolve, reject) => {
930
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
931
+ const jsonRpcResponsePromises = {};
932
+ const handleMessage = async (evt) => {
933
+ const responseBuffer = await decodeBytes(evt);
934
+ switch (state.__type) {
935
+ case 'reflector_id_received':
936
+ if (responseBuffer.byteLength !== 0) {
937
+ throw new Error('Encountered unexpected message while awaiting reflection');
938
+ }
939
+ const ecdhKeypair = await generateECDHKeypair();
940
+ const binaryMsg = await createHelloReq(ecdhKeypair.publicKey, associationKeypair.privateKey);
941
+ if (encoding == 'base64') {
942
+ socket.send(fromUint8Array$1(binaryMsg));
943
+ }
944
+ else {
945
+ socket.send(binaryMsg);
946
+ }
947
+ state = {
948
+ __type: 'hello_req_sent',
949
+ associationPublicKey: associationKeypair.publicKey,
950
+ ecdhPrivateKey: ecdhKeypair.privateKey,
951
+ };
952
+ break;
953
+ case 'connected':
954
+ try {
955
+ const sequenceNumberVector = responseBuffer.slice(0, SEQUENCE_NUMBER_BYTES);
956
+ const sequenceNumber = getSequenceNumberFromByteArray(sequenceNumberVector);
957
+ if (sequenceNumber !== (lastKnownInboundSequenceNumber + 1)) {
958
+ throw new Error('Encrypted message has invalid sequence number');
965
959
  }
966
- const ecdhKeypair = yield generateECDHKeypair();
967
- const binaryMsg = yield createHelloReq(ecdhKeypair.publicKey, associationKeypair.privateKey);
968
- if (encoding == 'base64') {
969
- socket.send(fromUint8Array$1(binaryMsg));
960
+ lastKnownInboundSequenceNumber = sequenceNumber;
961
+ const jsonRpcMessage = await decryptJsonRpcMessage(responseBuffer, state.sharedSecret);
962
+ const responsePromise = jsonRpcResponsePromises[jsonRpcMessage.id];
963
+ delete jsonRpcResponsePromises[jsonRpcMessage.id];
964
+ responsePromise.resolve(jsonRpcMessage.result);
965
+ }
966
+ catch (e) {
967
+ if (e instanceof SolanaMobileWalletAdapterProtocolError) {
968
+ const responsePromise = jsonRpcResponsePromises[e.jsonRpcMessageId];
969
+ delete jsonRpcResponsePromises[e.jsonRpcMessageId];
970
+ responsePromise.reject(e);
970
971
  }
971
972
  else {
972
- socket.send(binaryMsg);
973
+ throw e;
973
974
  }
974
- state = {
975
- __type: 'hello_req_sent',
976
- associationPublicKey: associationKeypair.publicKey,
977
- ecdhPrivateKey: ecdhKeypair.privateKey,
978
- };
979
- break;
980
- case 'connected':
981
- try {
982
- const sequenceNumberVector = responseBuffer.slice(0, SEQUENCE_NUMBER_BYTES);
975
+ }
976
+ break;
977
+ case 'hello_req_sent': {
978
+ const sharedSecret = await parseHelloRsp(responseBuffer, state.associationPublicKey, state.ecdhPrivateKey);
979
+ const sessionPropertiesBuffer = responseBuffer.slice(ENCODED_PUBLIC_KEY_LENGTH_BYTES);
980
+ const sessionProperties = sessionPropertiesBuffer.byteLength !== 0
981
+ ? await (async () => {
982
+ const sequenceNumberVector = sessionPropertiesBuffer.slice(0, SEQUENCE_NUMBER_BYTES);
983
983
  const sequenceNumber = getSequenceNumberFromByteArray(sequenceNumberVector);
984
984
  if (sequenceNumber !== (lastKnownInboundSequenceNumber + 1)) {
985
985
  throw new Error('Encrypted message has invalid sequence number');
986
986
  }
987
987
  lastKnownInboundSequenceNumber = sequenceNumber;
988
- const jsonRpcMessage = yield decryptJsonRpcMessage(responseBuffer, state.sharedSecret);
989
- const responsePromise = jsonRpcResponsePromises[jsonRpcMessage.id];
990
- delete jsonRpcResponsePromises[jsonRpcMessage.id];
991
- responsePromise.resolve(jsonRpcMessage.result);
988
+ return parseSessionProps(sessionPropertiesBuffer, sharedSecret);
989
+ })() : { protocol_version: 'legacy' };
990
+ state = { __type: 'connected', sharedSecret, sessionProperties };
991
+ const wallet = createMobileWalletProxy(sessionProperties.protocol_version, async (method, params) => {
992
+ const id = nextJsonRpcMessageId++;
993
+ const binaryMsg = await encryptJsonRpcMessage({
994
+ id,
995
+ jsonrpc: '2.0',
996
+ method,
997
+ params: params ?? {},
998
+ }, sharedSecret);
999
+ if (encoding == 'base64') {
1000
+ socket.send(fromUint8Array$1(binaryMsg));
992
1001
  }
993
- catch (e) {
994
- if (e instanceof SolanaMobileWalletAdapterProtocolError) {
995
- const responsePromise = jsonRpcResponsePromises[e.jsonRpcMessageId];
996
- delete jsonRpcResponsePromises[e.jsonRpcMessageId];
997
- responsePromise.reject(e);
998
- }
999
- else {
1000
- throw e;
1001
- }
1002
+ else {
1003
+ socket.send(binaryMsg);
1002
1004
  }
1003
- break;
1004
- case 'hello_req_sent': {
1005
- const sharedSecret = yield parseHelloRsp(responseBuffer, state.associationPublicKey, state.ecdhPrivateKey);
1006
- const sessionPropertiesBuffer = responseBuffer.slice(ENCODED_PUBLIC_KEY_LENGTH_BYTES);
1007
- const sessionProperties = sessionPropertiesBuffer.byteLength !== 0
1008
- ? yield (() => __awaiter(this, void 0, void 0, function* () {
1009
- const sequenceNumberVector = sessionPropertiesBuffer.slice(0, SEQUENCE_NUMBER_BYTES);
1010
- const sequenceNumber = getSequenceNumberFromByteArray(sequenceNumberVector);
1011
- if (sequenceNumber !== (lastKnownInboundSequenceNumber + 1)) {
1012
- throw new Error('Encrypted message has invalid sequence number');
1013
- }
1014
- lastKnownInboundSequenceNumber = sequenceNumber;
1015
- return parseSessionProps(sessionPropertiesBuffer, sharedSecret);
1016
- }))() : { protocol_version: 'legacy' };
1017
- state = { __type: 'connected', sharedSecret, sessionProperties };
1018
- const wallet = createMobileWalletProxy(sessionProperties.protocol_version, (method, params) => __awaiter(this, void 0, void 0, function* () {
1019
- const id = nextJsonRpcMessageId++;
1020
- const binaryMsg = yield encryptJsonRpcMessage({
1021
- id,
1022
- jsonrpc: '2.0',
1023
- method,
1024
- params: params !== null && params !== void 0 ? params : {},
1025
- }, sharedSecret);
1026
- if (encoding == 'base64') {
1027
- socket.send(fromUint8Array$1(binaryMsg));
1028
- }
1029
- else {
1030
- socket.send(binaryMsg);
1031
- }
1032
- return new Promise((resolve, reject) => {
1033
- jsonRpcResponsePromises[id] = {
1034
- resolve(result) {
1035
- switch (method) {
1036
- case 'authorize':
1037
- case 'reauthorize': {
1038
- const { wallet_uri_base } = result;
1039
- if (wallet_uri_base != null) {
1040
- try {
1041
- assertSecureEndpointSpecificURI(wallet_uri_base);
1042
- }
1043
- catch (e) {
1044
- reject(e);
1045
- return;
1046
- }
1005
+ return new Promise((resolve, reject) => {
1006
+ jsonRpcResponsePromises[id] = {
1007
+ resolve(result) {
1008
+ switch (method) {
1009
+ case 'authorize':
1010
+ case 'reauthorize': {
1011
+ const { wallet_uri_base } = result;
1012
+ if (wallet_uri_base != null) {
1013
+ try {
1014
+ assertSecureEndpointSpecificURI(wallet_uri_base);
1015
+ }
1016
+ catch (e) {
1017
+ reject(e);
1018
+ return;
1047
1019
  }
1048
- break;
1049
1020
  }
1021
+ break;
1050
1022
  }
1051
- resolve(result);
1052
- },
1053
- reject,
1054
- };
1055
- });
1056
- }));
1057
- sessionEstablished = true;
1058
- try {
1059
- resolve(wallet);
1060
- }
1061
- catch (e) {
1062
- reject(e);
1063
- }
1064
- break;
1023
+ }
1024
+ resolve(result);
1025
+ },
1026
+ reject,
1027
+ };
1028
+ });
1029
+ });
1030
+ sessionEstablished = true;
1031
+ try {
1032
+ resolve(wallet);
1065
1033
  }
1034
+ catch (e) {
1035
+ reject(e);
1036
+ }
1037
+ break;
1066
1038
  }
1067
- });
1068
- socket.addEventListener('message', handleMessage);
1069
- handleClose = () => {
1070
- socket.removeEventListener('message', handleMessage);
1071
- disposeSocket();
1072
- if (!sessionEstablished) {
1073
- reject(new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_SESSION_CLOSED, `The wallet session was closed before connection.`, { closeEvent: new CloseEvent('socket was closed before connection') }));
1074
- }
1075
- };
1076
- }) };
1077
- });
1039
+ }
1040
+ };
1041
+ socket.addEventListener('message', handleMessage);
1042
+ handleClose = () => {
1043
+ socket.removeEventListener('message', handleMessage);
1044
+ disposeSocket();
1045
+ if (!sessionEstablished) {
1046
+ reject(new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_SESSION_CLOSED, `The wallet session was closed before connection.`, { closeEvent: new CloseEvent('socket was closed before connection') }));
1047
+ }
1048
+ };
1049
+ }) };
1078
1050
  }
1079
1051
 
1080
- export { SolanaCloneAuthorization, SolanaMobileWalletAdapterError, SolanaMobileWalletAdapterErrorCode, SolanaMobileWalletAdapterProtocolError, SolanaMobileWalletAdapterProtocolErrorCode, SolanaSignInWithSolana, SolanaSignTransactions, startRemoteScenario, transact };
1052
+ export { SolanaCloneAuthorization, SolanaMobileWalletAdapterError, SolanaMobileWalletAdapterErrorCode, SolanaMobileWalletAdapterProtocolError, SolanaMobileWalletAdapterProtocolErrorCode, SolanaSignInWithSolana, SolanaSignTransactions, startRemoteScenario, startScenario, transact };