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