@pooflabs/web 0.0.75 → 0.0.77

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.
Files changed (83) hide show
  1. package/README.md +32 -0
  2. package/dist/auth/providers/sol/sol-utils.d.ts +2 -0
  3. package/dist/{index-BcDe_euX.js → index-Bfk81d8_.js} +883 -25
  4. package/dist/index-Bfk81d8_.js.map +1 -0
  5. package/dist/{index-Dho2J3X6.esm.js → index-CFh1x-M0.esm.js} +368 -162
  6. package/dist/index-CFh1x-M0.esm.js.map +1 -0
  7. package/dist/index-CdOv7Nw2.esm.js +6 -0
  8. package/dist/index-CdOv7Nw2.esm.js.map +1 -0
  9. package/dist/{index-_vhjpl1l.esm.js → index-CeQ8hE3s.esm.js} +881 -24
  10. package/dist/index-CeQ8hE3s.esm.js.map +1 -0
  11. package/dist/index-DDXzCx2W.js +8 -0
  12. package/dist/index-DDXzCx2W.js.map +1 -0
  13. package/dist/{index-CMeewi-G.js → index-DQVpAl5t.js} +368 -161
  14. package/dist/index-DQVpAl5t.js.map +1 -0
  15. package/dist/{index-BHYrnHi6.js → index-KUU0aVzP.js} +882 -24
  16. package/dist/index-KUU0aVzP.js.map +1 -0
  17. package/dist/{index-BqDvUK9s.esm.js → index-R7t9pRt_.esm.js} +882 -23
  18. package/dist/index-R7t9pRt_.esm.js.map +1 -0
  19. package/dist/{index.browser-Br0p4bjw.esm.js → index.browser-BE44CEaJ.esm.js} +3 -3
  20. package/dist/{index.browser-Br0p4bjw.esm.js.map → index.browser-BE44CEaJ.esm.js.map} +1 -1
  21. package/dist/{index.browser-DZjyUgtx.esm.js → index.browser-C-_FEr5M.esm.js} +449 -479
  22. package/dist/index.browser-C-_FEr5M.esm.js.map +1 -0
  23. package/dist/index.browser-C9bFQZyQ.esm.js +1373 -0
  24. package/dist/index.browser-C9bFQZyQ.esm.js.map +1 -0
  25. package/dist/index.browser-Dbq5Qf1G.esm.js +242 -0
  26. package/dist/index.browser-Dbq5Qf1G.esm.js.map +1 -0
  27. package/dist/index.browser-Df7yN8D5.js +245 -0
  28. package/dist/index.browser-Df7yN8D5.js.map +1 -0
  29. package/dist/{index.browser-BzHjnrpD.js → index.browser-Di1_YZpi.js} +3 -3
  30. package/dist/{index.browser-BzHjnrpD.js.map → index.browser-Di1_YZpi.js.map} +1 -1
  31. package/dist/{index.browser-BOJRGZWX.js → index.browser-DjEZSiqI.js} +449 -479
  32. package/dist/index.browser-DjEZSiqI.js.map +1 -0
  33. package/dist/index.browser-dszs5oe5.js +1376 -0
  34. package/dist/index.browser-dszs5oe5.js.map +1 -0
  35. package/dist/index.d.ts +1 -0
  36. package/dist/index.esm.js +1 -1
  37. package/dist/index.js +3 -1
  38. package/dist/index.js.map +1 -1
  39. package/dist/{index.native-DcKDTqvq.js → index.native-CKd2b3_F.js} +2767 -188
  40. package/dist/index.native-CKd2b3_F.js.map +1 -0
  41. package/dist/{index.native-BB7er4-z.esm.js → index.native-DAyMVhFq.esm.js} +2766 -190
  42. package/dist/index.native-DAyMVhFq.esm.js.map +1 -0
  43. package/dist/index.native.d.ts +1 -0
  44. package/dist/index.native.esm.js +1 -1
  45. package/dist/index.native.js +3 -1
  46. package/dist/index.native.js.map +1 -1
  47. package/dist/{phantom-wallet-provider-DMxFAUC4.js → phantom-wallet-provider-DJ6qf2VB.js} +4 -4
  48. package/dist/{phantom-wallet-provider-DMxFAUC4.js.map → phantom-wallet-provider-DJ6qf2VB.js.map} +1 -1
  49. package/dist/{phantom-wallet-provider-DHok8ui3.esm.js → phantom-wallet-provider-DQ0uhl2v.esm.js} +4 -4
  50. package/dist/{phantom-wallet-provider-DHok8ui3.esm.js.map → phantom-wallet-provider-DQ0uhl2v.esm.js.map} +1 -1
  51. package/dist/{privy-wallet-provider-CFuoQYuv.esm.js → privy-wallet-provider-BhiHnphv.esm.js} +7 -8
  52. package/dist/privy-wallet-provider-BhiHnphv.esm.js.map +1 -0
  53. package/dist/{privy-wallet-provider-Bhvw0t1d.js → privy-wallet-provider-CMCv5g3O.js} +7 -8
  54. package/dist/privy-wallet-provider-CMCv5g3O.js.map +1 -0
  55. package/dist/{solana-mobile-wallet-provider-BpQghAgC.esm.js → solana-mobile-wallet-provider-DSSk_6CR.esm.js} +4 -4
  56. package/dist/{solana-mobile-wallet-provider-BpQghAgC.esm.js.map → solana-mobile-wallet-provider-DSSk_6CR.esm.js.map} +1 -1
  57. package/dist/{solana-mobile-wallet-provider-D8b5y-By.js → solana-mobile-wallet-provider-DaFNesCe.js} +4 -4
  58. package/dist/{solana-mobile-wallet-provider-D8b5y-By.js.map → solana-mobile-wallet-provider-DaFNesCe.js.map} +1 -1
  59. package/package.json +2 -2
  60. package/dist/index-BHYrnHi6.js.map +0 -1
  61. package/dist/index-BcDe_euX.js.map +0 -1
  62. package/dist/index-Bdcc5821.js +0 -2375
  63. package/dist/index-Bdcc5821.js.map +0 -1
  64. package/dist/index-BqDvUK9s.esm.js.map +0 -1
  65. package/dist/index-CMeewi-G.js.map +0 -1
  66. package/dist/index-CrOVJFX9.esm.js +0 -2373
  67. package/dist/index-CrOVJFX9.esm.js.map +0 -1
  68. package/dist/index-Dho2J3X6.esm.js.map +0 -1
  69. package/dist/index-_vhjpl1l.esm.js.map +0 -1
  70. package/dist/index.browser-BOJRGZWX.js.map +0 -1
  71. package/dist/index.browser-Btm3sRKb.esm.js +0 -307
  72. package/dist/index.browser-Btm3sRKb.esm.js.map +0 -1
  73. package/dist/index.browser-CGfjPfzM.esm.js +0 -1468
  74. package/dist/index.browser-CGfjPfzM.esm.js.map +0 -1
  75. package/dist/index.browser-DZjyUgtx.esm.js.map +0 -1
  76. package/dist/index.browser-Dapjfbl6.js +0 -310
  77. package/dist/index.browser-Dapjfbl6.js.map +0 -1
  78. package/dist/index.browser-JX3F6oPV.js +0 -1471
  79. package/dist/index.browser-JX3F6oPV.js.map +0 -1
  80. package/dist/index.native-BB7er4-z.esm.js.map +0 -1
  81. package/dist/index.native-DcKDTqvq.js.map +0 -1
  82. package/dist/privy-wallet-provider-Bhvw0t1d.js.map +0 -1
  83. package/dist/privy-wallet-provider-CFuoQYuv.esm.js.map +0 -1
@@ -0,0 +1,1373 @@
1
+ import { VersionedMessage, Transaction, VersionedTransaction, SIGNATURE_LENGTH_IN_BYTES } from '@solana/web3.js';
2
+
3
+ /**
4
+ * TODO: docs
5
+ */
6
+ function createSignInMessageText(input) {
7
+ // ${domain} wants you to sign in with your Solana account:
8
+ // ${address}
9
+ //
10
+ // ${statement}
11
+ //
12
+ // URI: ${uri}
13
+ // Version: ${version}
14
+ // Chain ID: ${chain}
15
+ // Nonce: ${nonce}
16
+ // Issued At: ${issued-at}
17
+ // Expiration Time: ${expiration-time}
18
+ // Not Before: ${not-before}
19
+ // Request ID: ${request-id}
20
+ // Resources:
21
+ // - ${resources[0]}
22
+ // - ${resources[1]}
23
+ // ...
24
+ // - ${resources[n]}
25
+ let message = `${input.domain} wants you to sign in with your Solana account:\n`;
26
+ message += `${input.address}`;
27
+ if (input.statement) {
28
+ message += `\n\n${input.statement}`;
29
+ }
30
+ const fields = [];
31
+ if (input.uri) {
32
+ fields.push(`URI: ${input.uri}`);
33
+ }
34
+ if (input.version) {
35
+ fields.push(`Version: ${input.version}`);
36
+ }
37
+ if (input.chainId) {
38
+ fields.push(`Chain ID: ${input.chainId}`);
39
+ }
40
+ if (input.nonce) {
41
+ fields.push(`Nonce: ${input.nonce}`);
42
+ }
43
+ if (input.issuedAt) {
44
+ fields.push(`Issued At: ${input.issuedAt}`);
45
+ }
46
+ if (input.expirationTime) {
47
+ fields.push(`Expiration Time: ${input.expirationTime}`);
48
+ }
49
+ if (input.notBefore) {
50
+ fields.push(`Not Before: ${input.notBefore}`);
51
+ }
52
+ if (input.requestId) {
53
+ fields.push(`Request ID: ${input.requestId}`);
54
+ }
55
+ if (input.resources) {
56
+ fields.push(`Resources:`);
57
+ for (const resource of input.resources) {
58
+ fields.push(`- ${resource}`);
59
+ }
60
+ }
61
+ if (fields.length) {
62
+ message += `\n\n${fields.join('\n')}`;
63
+ }
64
+ return message;
65
+ }
66
+
67
+ function createDecoder(decoder) {
68
+ return Object.freeze({
69
+ ...decoder,
70
+ decode: (bytes, offset = 0) => decoder.read(bytes, offset)[0]
71
+ });
72
+ }
73
+
74
+ var getBaseXDecoder = (alphabet4) => {
75
+ return createDecoder({
76
+ read(rawBytes, offset) {
77
+ const bytes = offset === 0 ? rawBytes : rawBytes.slice(offset);
78
+ if (bytes.length === 0) return ["", 0];
79
+ let trailIndex = bytes.findIndex((n) => n !== 0);
80
+ trailIndex = trailIndex === -1 ? bytes.length : trailIndex;
81
+ const leadingZeroes = alphabet4[0].repeat(trailIndex);
82
+ if (trailIndex === bytes.length) return [leadingZeroes, rawBytes.length];
83
+ const base10Number = bytes.slice(trailIndex).reduce((sum, byte) => sum * 256n + BigInt(byte), 0n);
84
+ const tailChars = getBaseXFromBigInt(base10Number, alphabet4);
85
+ return [leadingZeroes + tailChars, rawBytes.length];
86
+ }
87
+ });
88
+ };
89
+ function getBaseXFromBigInt(value, alphabet4) {
90
+ const base = BigInt(alphabet4.length);
91
+ const tailChars = [];
92
+ while (value > 0n) {
93
+ tailChars.unshift(alphabet4[Number(value % base)]);
94
+ value /= base;
95
+ }
96
+ return tailChars.join("");
97
+ }
98
+
99
+ // src/base58.ts
100
+ var alphabet2 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
101
+ var getBase58Decoder = () => getBaseXDecoder(alphabet2);
102
+
103
+ // Typescript `enums` thwart tree-shaking. See https://bargsten.org/jsts/enums/
104
+ const SolanaMobileWalletAdapterErrorCode = {
105
+ ERROR_ASSOCIATION_PORT_OUT_OF_RANGE: 'ERROR_ASSOCIATION_PORT_OUT_OF_RANGE',
106
+ ERROR_FORBIDDEN_WALLET_BASE_URL: 'ERROR_FORBIDDEN_WALLET_BASE_URL',
107
+ ERROR_SECURE_CONTEXT_REQUIRED: 'ERROR_SECURE_CONTEXT_REQUIRED',
108
+ ERROR_SESSION_CLOSED: 'ERROR_SESSION_CLOSED',
109
+ ERROR_SESSION_TIMEOUT: 'ERROR_SESSION_TIMEOUT',
110
+ ERROR_WALLET_NOT_FOUND: 'ERROR_WALLET_NOT_FOUND',
111
+ ERROR_INVALID_PROTOCOL_VERSION: 'ERROR_INVALID_PROTOCOL_VERSION'};
112
+ class SolanaMobileWalletAdapterError extends Error {
113
+ data;
114
+ code;
115
+ constructor(...args) {
116
+ const [code, message, data] = args;
117
+ super(message);
118
+ this.code = code;
119
+ this.data = data;
120
+ this.name = 'SolanaMobileWalletAdapterError';
121
+ }
122
+ }
123
+ class SolanaMobileWalletAdapterProtocolError extends Error {
124
+ data;
125
+ code;
126
+ jsonRpcMessageId;
127
+ constructor(...args) {
128
+ const [jsonRpcMessageId, code, message, data] = args;
129
+ super(message);
130
+ this.code = code;
131
+ this.data = data;
132
+ this.jsonRpcMessageId = jsonRpcMessageId;
133
+ this.name = 'SolanaMobileWalletAdapterProtocolError';
134
+ }
135
+ }
136
+
137
+ function encode(input) {
138
+ return window.btoa(input);
139
+ }
140
+ function fromUint8Array$1(byteArray, urlsafe) {
141
+ const base64 = window.btoa(String.fromCharCode.call(null, ...byteArray));
142
+ if (urlsafe) {
143
+ return base64
144
+ .replace(/\+/g, '-')
145
+ .replace(/\//g, '_')
146
+ .replace(/=+$/, '');
147
+ }
148
+ else
149
+ return base64;
150
+ }
151
+ function toUint8Array$1(base64EncodedByteArray) {
152
+ return new Uint8Array(window
153
+ .atob(base64EncodedByteArray)
154
+ .split('')
155
+ .map((c) => c.charCodeAt(0)));
156
+ }
157
+
158
+ async function createHelloReq(ecdhPublicKey, associationKeypairPrivateKey) {
159
+ const publicKeyBuffer = await crypto.subtle.exportKey('raw', ecdhPublicKey);
160
+ const signatureBuffer = await crypto.subtle.sign({ hash: 'SHA-256', name: 'ECDSA' }, associationKeypairPrivateKey, publicKeyBuffer);
161
+ const response = new Uint8Array(publicKeyBuffer.byteLength + signatureBuffer.byteLength);
162
+ response.set(new Uint8Array(publicKeyBuffer), 0);
163
+ response.set(new Uint8Array(signatureBuffer), publicKeyBuffer.byteLength);
164
+ return response;
165
+ }
166
+
167
+ function createSIWSMessage(payload) {
168
+ return createSignInMessageText(payload);
169
+ }
170
+ function createSIWSMessageBase64Url(payload) {
171
+ return encode(createSIWSMessage(payload))
172
+ .replace(/\+/g, '-')
173
+ .replace(/\//g, '_')
174
+ .replace(/=+$/, ''); // convert to base64url encoding;
175
+ }
176
+
177
+ // optional features
178
+ const SolanaSignTransactions = 'solana:signTransactions';
179
+ const SolanaCloneAuthorization = 'solana:cloneAuthorization';
180
+
181
+ function fromUint8Array$2(byteArray) {
182
+ return getBase58Decoder().decode(byteArray);
183
+ }
184
+ function base64ToBase58(base64EncodedString) {
185
+ return fromUint8Array$2(toUint8Array$1(base64EncodedString));
186
+ }
187
+
188
+ /**
189
+ * Creates a {@link MobileWallet} proxy that handles backwards compatibility and API to RPC conversion.
190
+ *
191
+ * @param protocolVersion the protocol version in use for this session/request
192
+ * @param protocolRequestHandler callback function that handles sending the RPC request to the wallet endpoint.
193
+ * @returns a {@link MobileWallet} proxy
194
+ */
195
+ function createMobileWalletProxy(protocolVersion, protocolRequestHandler) {
196
+ return new Proxy({}, {
197
+ get(target, p) {
198
+ // Wrapping a Proxy in a promise results in the Proxy being asked for a 'then' property so must
199
+ // return null if 'then' is called on this proxy to let the 'resolve()' call know this is not a promise.
200
+ // see: https://stackoverflow.com/a/53890904
201
+ //@ts-ignore
202
+ if (p === 'then') {
203
+ return null;
204
+ }
205
+ if (target[p] == null) {
206
+ target[p] = async function (inputParams) {
207
+ const { method, params } = handleMobileWalletRequest(p, inputParams, protocolVersion);
208
+ const result = await protocolRequestHandler(method, params);
209
+ // if the request tried to sign in but the wallet did not return a sign in result, fallback on message signing
210
+ if (method === 'authorize' && params.sign_in_payload && !result.sign_in_result) {
211
+ result['sign_in_result'] = await signInFallback(params.sign_in_payload, result, protocolRequestHandler);
212
+ }
213
+ return handleMobileWalletResponse(p, result, protocolVersion);
214
+ };
215
+ }
216
+ return target[p];
217
+ },
218
+ defineProperty() {
219
+ return false;
220
+ },
221
+ deleteProperty() {
222
+ return false;
223
+ },
224
+ });
225
+ }
226
+ /**
227
+ * Handles all {@link MobileWallet} API requests and determines the correct MWA RPC method and params to call.
228
+ * This handles backwards compatibility, based on the provided @protocolVersion.
229
+ *
230
+ * @param methodName the name of {@link MobileWallet} method that was called
231
+ * @param methodParams the parameters that were passed to the method
232
+ * @param protocolVersion the protocol version in use for this session/request
233
+ * @returns the RPC request method and params that should be sent to the wallet endpoint
234
+ */
235
+ function handleMobileWalletRequest(methodName, methodParams, protocolVersion) {
236
+ let params = methodParams;
237
+ let method = methodName
238
+ .toString()
239
+ .replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)
240
+ .toLowerCase();
241
+ switch (methodName) {
242
+ case 'authorize': {
243
+ let { chain } = params;
244
+ if (protocolVersion === 'legacy') {
245
+ switch (chain) {
246
+ case 'solana:testnet': {
247
+ chain = 'testnet';
248
+ break;
249
+ }
250
+ case 'solana:devnet': {
251
+ chain = 'devnet';
252
+ break;
253
+ }
254
+ case 'solana:mainnet': {
255
+ chain = 'mainnet-beta';
256
+ break;
257
+ }
258
+ default: {
259
+ chain = params.cluster;
260
+ }
261
+ }
262
+ params.cluster = chain;
263
+ }
264
+ else {
265
+ switch (chain) {
266
+ case 'testnet':
267
+ case 'devnet': {
268
+ chain = `solana:${chain}`;
269
+ break;
270
+ }
271
+ case 'mainnet-beta': {
272
+ chain = 'solana:mainnet';
273
+ break;
274
+ }
275
+ }
276
+ params.chain = chain;
277
+ }
278
+ }
279
+ case 'reauthorize': {
280
+ const { auth_token, identity } = params;
281
+ if (auth_token) {
282
+ switch (protocolVersion) {
283
+ case 'legacy': {
284
+ method = 'reauthorize';
285
+ params = { auth_token: auth_token, identity: identity };
286
+ break;
287
+ }
288
+ default: {
289
+ method = 'authorize';
290
+ break;
291
+ }
292
+ }
293
+ }
294
+ break;
295
+ }
296
+ }
297
+ return { method, params };
298
+ }
299
+ /**
300
+ * Handles all {@link MobileWallet} API responses and modifies the response for backwards compatibility, if needed
301
+ *
302
+ * @param method the {@link MobileWallet} method that was called
303
+ * @param response the original response that was returned by the method call
304
+ * @param protocolVersion the protocol version in use for this session/request
305
+ * @returns the possibly modified response
306
+ */
307
+ function handleMobileWalletResponse(method, response, protocolVersion) {
308
+ switch (method) {
309
+ case 'getCapabilities': {
310
+ const capabilities = response;
311
+ switch (protocolVersion) {
312
+ case 'legacy': {
313
+ const features = [SolanaSignTransactions];
314
+ if (capabilities.supports_clone_authorization === true) {
315
+ features.push(SolanaCloneAuthorization);
316
+ }
317
+ return {
318
+ ...capabilities,
319
+ features: features,
320
+ };
321
+ }
322
+ case 'v1': {
323
+ return {
324
+ ...capabilities,
325
+ supports_sign_and_send_transactions: true,
326
+ supports_clone_authorization: capabilities.features.includes(SolanaCloneAuthorization)
327
+ };
328
+ }
329
+ }
330
+ }
331
+ }
332
+ return response;
333
+ }
334
+ async function signInFallback(signInPayload, authorizationResult, protocolRequestHandler) {
335
+ const domain = signInPayload.domain ?? window.location.host;
336
+ const address = authorizationResult.accounts[0].address;
337
+ const siwsMessage = createSIWSMessageBase64Url({ ...signInPayload, domain, address: base64ToBase58(address) });
338
+ const signMessageResult = await protocolRequestHandler('sign_messages', {
339
+ addresses: [address],
340
+ payloads: [siwsMessage]
341
+ });
342
+ const signedPayload = toUint8Array$1(signMessageResult.signed_payloads[0]);
343
+ const signedMessage = fromUint8Array$1(signedPayload.slice(0, signedPayload.length - 64));
344
+ const signature = fromUint8Array$1(signedPayload.slice(signedPayload.length - 64));
345
+ const signInResult = {
346
+ address: address,
347
+ // Workaround: some wallets have been observed to only reply with the message signature.
348
+ // This is non-compliant with the spec, but in the interest of maximizing compatibility,
349
+ // detect this case and reuse the original message.
350
+ signed_message: signedMessage.length == 0 ? siwsMessage : signedMessage,
351
+ signature
352
+ };
353
+ return signInResult;
354
+ }
355
+
356
+ const SEQUENCE_NUMBER_BYTES = 4;
357
+ function createSequenceNumberVector(sequenceNumber) {
358
+ if (sequenceNumber >= 4294967296) {
359
+ throw new Error('Outbound sequence number overflow. The maximum sequence number is 32-bytes.');
360
+ }
361
+ const byteArray = new ArrayBuffer(SEQUENCE_NUMBER_BYTES);
362
+ const view = new DataView(byteArray);
363
+ view.setUint32(0, sequenceNumber, /* littleEndian */ false);
364
+ return new Uint8Array(byteArray);
365
+ }
366
+
367
+ const INITIALIZATION_VECTOR_BYTES = 12;
368
+ const ENCODED_PUBLIC_KEY_LENGTH_BYTES = 65;
369
+ async function encryptMessage(plaintext, sequenceNumber, sharedSecret) {
370
+ const sequenceNumberVector = createSequenceNumberVector(sequenceNumber);
371
+ const initializationVector = new Uint8Array(INITIALIZATION_VECTOR_BYTES);
372
+ crypto.getRandomValues(initializationVector);
373
+ const ciphertext = await crypto.subtle.encrypt(getAlgorithmParams(sequenceNumberVector, initializationVector), sharedSecret, new TextEncoder().encode(plaintext));
374
+ const response = new Uint8Array(sequenceNumberVector.byteLength + initializationVector.byteLength + ciphertext.byteLength);
375
+ response.set(new Uint8Array(sequenceNumberVector), 0);
376
+ response.set(new Uint8Array(initializationVector), sequenceNumberVector.byteLength);
377
+ response.set(new Uint8Array(ciphertext), sequenceNumberVector.byteLength + initializationVector.byteLength);
378
+ return response;
379
+ }
380
+ async function decryptMessage(message, sharedSecret) {
381
+ const sequenceNumberVector = message.slice(0, SEQUENCE_NUMBER_BYTES);
382
+ const initializationVector = message.slice(SEQUENCE_NUMBER_BYTES, SEQUENCE_NUMBER_BYTES + INITIALIZATION_VECTOR_BYTES);
383
+ const ciphertext = message.slice(SEQUENCE_NUMBER_BYTES + INITIALIZATION_VECTOR_BYTES);
384
+ const plaintextBuffer = await crypto.subtle.decrypt(getAlgorithmParams(sequenceNumberVector, initializationVector), sharedSecret, ciphertext);
385
+ const plaintext = getUtf8Decoder().decode(plaintextBuffer);
386
+ return plaintext;
387
+ }
388
+ function getAlgorithmParams(sequenceNumber, initializationVector) {
389
+ return {
390
+ additionalData: sequenceNumber,
391
+ iv: initializationVector,
392
+ name: 'AES-GCM',
393
+ tagLength: 128, // 16 byte tag => 128 bits
394
+ };
395
+ }
396
+ let _utf8Decoder;
397
+ function getUtf8Decoder() {
398
+ if (_utf8Decoder === undefined) {
399
+ _utf8Decoder = new TextDecoder('utf-8');
400
+ }
401
+ return _utf8Decoder;
402
+ }
403
+
404
+ async function generateAssociationKeypair() {
405
+ return await crypto.subtle.generateKey({
406
+ name: 'ECDSA',
407
+ namedCurve: 'P-256',
408
+ }, false /* extractable */, ['sign'] /* keyUsages */);
409
+ }
410
+
411
+ async function generateECDHKeypair() {
412
+ return await crypto.subtle.generateKey({
413
+ name: 'ECDH',
414
+ namedCurve: 'P-256',
415
+ }, false /* extractable */, ['deriveKey', 'deriveBits'] /* keyUsages */);
416
+ }
417
+
418
+ // https://stackoverflow.com/a/9458996/802047
419
+ function arrayBufferToBase64String(buffer) {
420
+ let binary = '';
421
+ const bytes = new Uint8Array(buffer);
422
+ const len = bytes.byteLength;
423
+ for (let ii = 0; ii < len; ii++) {
424
+ binary += String.fromCharCode(bytes[ii]);
425
+ }
426
+ return window.btoa(binary);
427
+ }
428
+
429
+ function getRandomAssociationPort() {
430
+ return assertAssociationPort(49152 + Math.floor(Math.random() * (65535 - 49152 + 1)));
431
+ }
432
+ function assertAssociationPort(port) {
433
+ if (port < 49152 || port > 65535) {
434
+ throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_ASSOCIATION_PORT_OUT_OF_RANGE, `Association port number must be between 49152 and 65535. ${port} given.`, { port });
435
+ }
436
+ return port;
437
+ }
438
+
439
+ function getStringWithURLUnsafeCharactersReplaced(unsafeBase64EncodedString) {
440
+ return unsafeBase64EncodedString.replace(/[/+=]/g, (m) => ({
441
+ '/': '_',
442
+ '+': '-',
443
+ '=': '.',
444
+ }[m]));
445
+ }
446
+
447
+ const INTENT_NAME = 'solana-wallet';
448
+ function getPathParts(pathString) {
449
+ return (pathString
450
+ // Strip leading and trailing slashes
451
+ .replace(/(^\/+|\/+$)/g, '')
452
+ // Return an array of directories
453
+ .split('/'));
454
+ }
455
+ function getIntentURL(methodPathname, intentUrlBase) {
456
+ let baseUrl = null;
457
+ if (intentUrlBase) {
458
+ try {
459
+ baseUrl = new URL(intentUrlBase);
460
+ }
461
+ catch { } // eslint-disable-line no-empty
462
+ if (baseUrl?.protocol !== 'https:') {
463
+ throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_FORBIDDEN_WALLET_BASE_URL, 'Base URLs supplied by wallets must be valid `https` URLs');
464
+ }
465
+ }
466
+ baseUrl ||= new URL(`${INTENT_NAME}:/`);
467
+ const pathname = methodPathname.startsWith('/')
468
+ ? // Method is an absolute path. Replace it wholesale.
469
+ methodPathname
470
+ : // Method is a relative path. Merge it with the existing one.
471
+ [...getPathParts(baseUrl.pathname), ...getPathParts(methodPathname)].join('/');
472
+ return new URL(pathname, baseUrl);
473
+ }
474
+ async function getAssociateAndroidIntentURL(associationPublicKey, putativePort, associationURLBase, protocolVersions = ['v1']) {
475
+ const associationPort = assertAssociationPort(putativePort);
476
+ const exportedKey = await crypto.subtle.exportKey('raw', associationPublicKey);
477
+ const encodedKey = arrayBufferToBase64String(exportedKey);
478
+ const url = getIntentURL('v1/associate/local', associationURLBase);
479
+ url.searchParams.set('association', getStringWithURLUnsafeCharactersReplaced(encodedKey));
480
+ url.searchParams.set('port', `${associationPort}`);
481
+ protocolVersions.forEach((version) => {
482
+ url.searchParams.set('v', version);
483
+ });
484
+ return url;
485
+ }
486
+ async function getRemoteAssociateAndroidIntentURL(associationPublicKey, hostAuthority, reflectorId, associationURLBase, protocolVersions = ['v1']) {
487
+ const exportedKey = await crypto.subtle.exportKey('raw', associationPublicKey);
488
+ const encodedKey = arrayBufferToBase64String(exportedKey);
489
+ const url = getIntentURL('v1/associate/remote', associationURLBase);
490
+ url.searchParams.set('association', getStringWithURLUnsafeCharactersReplaced(encodedKey));
491
+ url.searchParams.set('reflector', `${hostAuthority}`);
492
+ url.searchParams.set('id', `${fromUint8Array$1(reflectorId, true)}`);
493
+ protocolVersions.forEach((version) => {
494
+ url.searchParams.set('v', version);
495
+ });
496
+ return url;
497
+ }
498
+
499
+ async function encryptJsonRpcMessage(jsonRpcMessage, sharedSecret) {
500
+ const plaintext = JSON.stringify(jsonRpcMessage);
501
+ const sequenceNumber = jsonRpcMessage.id;
502
+ return encryptMessage(plaintext, sequenceNumber, sharedSecret);
503
+ }
504
+ async function decryptJsonRpcMessage(message, sharedSecret) {
505
+ const plaintext = await decryptMessage(message, sharedSecret);
506
+ const jsonRpcMessage = JSON.parse(plaintext);
507
+ if (Object.hasOwnProperty.call(jsonRpcMessage, 'error')) {
508
+ throw new SolanaMobileWalletAdapterProtocolError(jsonRpcMessage.id, jsonRpcMessage.error.code, jsonRpcMessage.error.message);
509
+ }
510
+ return jsonRpcMessage;
511
+ }
512
+
513
+ async function parseHelloRsp(payloadBuffer, // The X9.62-encoded wallet endpoint ephemeral ECDH public keypoint.
514
+ associationPublicKey, ecdhPrivateKey) {
515
+ const [associationPublicKeyBuffer, walletPublicKey] = await Promise.all([
516
+ crypto.subtle.exportKey('raw', associationPublicKey),
517
+ crypto.subtle.importKey('raw', payloadBuffer.slice(0, ENCODED_PUBLIC_KEY_LENGTH_BYTES), { name: 'ECDH', namedCurve: 'P-256' }, false /* extractable */, [] /* keyUsages */),
518
+ ]);
519
+ const sharedSecret = await crypto.subtle.deriveBits({ name: 'ECDH', public: walletPublicKey }, ecdhPrivateKey, 256);
520
+ const ecdhSecretKey = await crypto.subtle.importKey('raw', sharedSecret, 'HKDF', false /* extractable */, ['deriveKey'] /* keyUsages */);
521
+ const aesKeyMaterialVal = await crypto.subtle.deriveKey({
522
+ name: 'HKDF',
523
+ hash: 'SHA-256',
524
+ salt: new Uint8Array(associationPublicKeyBuffer),
525
+ info: new Uint8Array(),
526
+ }, ecdhSecretKey, { name: 'AES-GCM', length: 128 }, false /* extractable */, ['encrypt', 'decrypt']);
527
+ return aesKeyMaterialVal;
528
+ }
529
+
530
+ async function parseSessionProps(message, sharedSecret) {
531
+ const plaintext = await decryptMessage(message, sharedSecret);
532
+ const jsonProperties = JSON.parse(plaintext);
533
+ let protocolVersion = 'legacy';
534
+ if (Object.hasOwnProperty.call(jsonProperties, 'v')) {
535
+ switch (jsonProperties.v) {
536
+ case 1:
537
+ case '1':
538
+ case 'v1':
539
+ protocolVersion = 'v1';
540
+ break;
541
+ case 'legacy':
542
+ protocolVersion = 'legacy';
543
+ break;
544
+ default:
545
+ throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_INVALID_PROTOCOL_VERSION, `Unknown/unsupported protocol version: ${jsonProperties.v}`);
546
+ }
547
+ }
548
+ return ({
549
+ protocol_version: protocolVersion
550
+ });
551
+ }
552
+
553
+ // Typescript `enums` thwart tree-shaking. See https://bargsten.org/jsts/enums/
554
+ const Browser = {
555
+ Firefox: 0,
556
+ Other: 1,
557
+ };
558
+ function assertUnreachable(x) {
559
+ return x;
560
+ }
561
+ function getBrowser() {
562
+ return navigator.userAgent.indexOf('Firefox/') !== -1 ? Browser.Firefox : Browser.Other;
563
+ }
564
+ function getDetectionPromise() {
565
+ // Chrome and others silently fail if a custom protocol is not supported.
566
+ // For these, we wait to see if the browser is navigated away from in
567
+ // a reasonable amount of time (ie. the native wallet opened).
568
+ return new Promise((resolve, reject) => {
569
+ function cleanup() {
570
+ clearTimeout(timeoutId);
571
+ window.removeEventListener('blur', handleBlur);
572
+ }
573
+ function handleBlur() {
574
+ cleanup();
575
+ resolve();
576
+ }
577
+ window.addEventListener('blur', handleBlur);
578
+ const timeoutId = setTimeout(() => {
579
+ cleanup();
580
+ reject();
581
+ }, 3000);
582
+ });
583
+ }
584
+ let _frame = null;
585
+ function launchUrlThroughHiddenFrame(url) {
586
+ if (_frame == null) {
587
+ _frame = document.createElement('iframe');
588
+ _frame.style.display = 'none';
589
+ document.body.appendChild(_frame);
590
+ }
591
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
592
+ _frame.contentWindow.location.href = url.toString();
593
+ }
594
+ async function launchAssociation(associationUrl) {
595
+ if (associationUrl.protocol === 'https:') {
596
+ // The association URL is an Android 'App Link' or iOS 'Universal Link'.
597
+ // These are regular web URLs that are designed to launch an app if it
598
+ // is installed or load the actual target webpage if not.
599
+ window.location.assign(associationUrl);
600
+ }
601
+ else {
602
+ // The association URL has a custom protocol (eg. `solana-wallet:`)
603
+ try {
604
+ const browser = getBrowser();
605
+ switch (browser) {
606
+ case Browser.Firefox:
607
+ // If a custom protocol is not supported in Firefox, it throws.
608
+ launchUrlThroughHiddenFrame(associationUrl);
609
+ // If we reached this line, it's supported.
610
+ break;
611
+ case Browser.Other: {
612
+ const detectionPromise = getDetectionPromise();
613
+ window.location.assign(associationUrl);
614
+ await detectionPromise;
615
+ break;
616
+ }
617
+ default:
618
+ assertUnreachable(browser);
619
+ }
620
+ }
621
+ catch (e) {
622
+ throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_WALLET_NOT_FOUND, 'Found no installed wallet that supports the mobile wallet protocol.');
623
+ }
624
+ }
625
+ }
626
+ async function startSession(associationPublicKey, associationURLBase) {
627
+ const randomAssociationPort = getRandomAssociationPort();
628
+ const associationUrl = await getAssociateAndroidIntentURL(associationPublicKey, randomAssociationPort, associationURLBase);
629
+ await launchAssociation(associationUrl);
630
+ return randomAssociationPort;
631
+ }
632
+
633
+ const WEBSOCKET_CONNECTION_CONFIG = {
634
+ /**
635
+ * 300 milliseconds is a generally accepted threshold for what someone
636
+ * would consider an acceptable response time for a user interface
637
+ * after having performed a low-attention tapping task. We set the initial
638
+ * interval at which we wait for the wallet to set up the websocket at
639
+ * half this, as per the Nyquist frequency, with a progressive backoff
640
+ * sequence from there. The total wait time is 30s, which allows for the
641
+ * user to be presented with a disambiguation dialog, select a wallet, and
642
+ * for the wallet app to subsequently start.
643
+ */
644
+ retryDelayScheduleMs: [150, 150, 200, 500, 500, 750, 750, 1000],
645
+ timeoutMs: 30000,
646
+ };
647
+ const WEBSOCKET_PROTOCOL_BINARY = 'com.solana.mobilewalletadapter.v1';
648
+ const WEBSOCKET_PROTOCOL_BASE64 = 'com.solana.mobilewalletadapter.v1.base64';
649
+ function assertSecureContext() {
650
+ if (typeof window === 'undefined' || window.isSecureContext !== true) {
651
+ throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_SECURE_CONTEXT_REQUIRED, 'The mobile wallet adapter protocol must be used in a secure context (`https`).');
652
+ }
653
+ }
654
+ function assertSecureEndpointSpecificURI(walletUriBase) {
655
+ let url;
656
+ try {
657
+ url = new URL(walletUriBase);
658
+ }
659
+ catch {
660
+ throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_FORBIDDEN_WALLET_BASE_URL, 'Invalid base URL supplied by wallet');
661
+ }
662
+ if (url.protocol !== 'https:') {
663
+ throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_FORBIDDEN_WALLET_BASE_URL, 'Base URLs supplied by wallets must be valid `https` URLs');
664
+ }
665
+ }
666
+ function getSequenceNumberFromByteArray(byteArray) {
667
+ const view = new DataView(byteArray);
668
+ return view.getUint32(0, /* littleEndian */ false);
669
+ }
670
+ function decodeVarLong(byteArray) {
671
+ var bytes = new Uint8Array(byteArray), l = byteArray.byteLength, limit = 10, value = 0, offset = 0, b;
672
+ do {
673
+ if (offset >= l || offset > limit)
674
+ throw new RangeError('Failed to decode varint');
675
+ b = bytes[offset++];
676
+ value |= (b & 0x7F) << (7 * offset);
677
+ } while (b >= 0x80);
678
+ return { value, offset };
679
+ }
680
+ function getReflectorIdFromByteArray(byteArray) {
681
+ let { value: length, offset } = decodeVarLong(byteArray);
682
+ return new Uint8Array(byteArray.slice(offset, offset + length));
683
+ }
684
+ async function transact$1(callback, config) {
685
+ const { wallet, close } = await startScenario(config);
686
+ try {
687
+ return await callback(await wallet);
688
+ }
689
+ finally {
690
+ close();
691
+ }
692
+ }
693
+ async function startScenario(config) {
694
+ assertSecureContext();
695
+ const associationKeypair = await generateAssociationKeypair();
696
+ const sessionPort = await startSession(associationKeypair.publicKey, config?.baseUri);
697
+ const websocketURL = `ws://localhost:${sessionPort}/solana-wallet`;
698
+ let connectionStartTime;
699
+ const getNextRetryDelayMs = (() => {
700
+ const schedule = [...WEBSOCKET_CONNECTION_CONFIG.retryDelayScheduleMs];
701
+ return () => (schedule.length > 1 ? schedule.shift() : schedule[0]);
702
+ })();
703
+ let nextJsonRpcMessageId = 1;
704
+ let lastKnownInboundSequenceNumber = 0;
705
+ let state = { __type: 'disconnected' };
706
+ let socket;
707
+ let sessionEstablished = false;
708
+ let handleForceClose;
709
+ return { close: () => {
710
+ socket.close();
711
+ handleForceClose();
712
+ }, wallet: new Promise((resolve, reject) => {
713
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
714
+ const jsonRpcResponsePromises = {};
715
+ const handleOpen = async () => {
716
+ if (state.__type !== 'connecting') {
717
+ console.warn('Expected adapter state to be `connecting` at the moment the websocket opens. ' +
718
+ `Got \`${state.__type}\`.`);
719
+ return;
720
+ }
721
+ socket.removeEventListener('open', handleOpen);
722
+ // previous versions of this library and walletlib incorrectly implemented the MWA session
723
+ // establishment protocol for local connections. The dapp is supposed to wait for the
724
+ // APP_PING message before sending the HELLO_REQ. Instead, the dapp was sending the HELLO_REQ
725
+ // immediately upon connection to the websocket server regardless of wether or not an
726
+ // APP_PING was sent by the wallet/websocket server. We must continue to support this behavior
727
+ // in case the user is using a wallet that has not updated their walletlib implementation.
728
+ const { associationKeypair } = state;
729
+ const ecdhKeypair = await generateECDHKeypair();
730
+ socket.send(await createHelloReq(ecdhKeypair.publicKey, associationKeypair.privateKey));
731
+ state = {
732
+ __type: 'hello_req_sent',
733
+ associationPublicKey: associationKeypair.publicKey,
734
+ ecdhPrivateKey: ecdhKeypair.privateKey,
735
+ };
736
+ };
737
+ const handleClose = (evt) => {
738
+ if (evt.wasClean) {
739
+ state = { __type: 'disconnected' };
740
+ }
741
+ else {
742
+ reject(new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_SESSION_CLOSED, `The wallet session dropped unexpectedly (${evt.code}: ${evt.reason}).`, { closeEvent: evt }));
743
+ }
744
+ disposeSocket();
745
+ };
746
+ const handleError = async (_evt) => {
747
+ disposeSocket();
748
+ if (Date.now() - connectionStartTime >= WEBSOCKET_CONNECTION_CONFIG.timeoutMs) {
749
+ reject(new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_SESSION_TIMEOUT, `Failed to connect to the wallet websocket at ${websocketURL}.`));
750
+ }
751
+ else {
752
+ await new Promise((resolve) => {
753
+ const retryDelayMs = getNextRetryDelayMs();
754
+ retryWaitTimeoutId = window.setTimeout(resolve, retryDelayMs);
755
+ });
756
+ attemptSocketConnection();
757
+ }
758
+ };
759
+ const handleMessage = async (evt) => {
760
+ const responseBuffer = await evt.data.arrayBuffer();
761
+ switch (state.__type) {
762
+ case 'connecting':
763
+ if (responseBuffer.byteLength !== 0) {
764
+ throw new Error('Encountered unexpected message while connecting');
765
+ }
766
+ const ecdhKeypair = await generateECDHKeypair();
767
+ socket.send(await createHelloReq(ecdhKeypair.publicKey, associationKeypair.privateKey));
768
+ state = {
769
+ __type: 'hello_req_sent',
770
+ associationPublicKey: associationKeypair.publicKey,
771
+ ecdhPrivateKey: ecdhKeypair.privateKey,
772
+ };
773
+ break;
774
+ case 'connected':
775
+ try {
776
+ const sequenceNumberVector = responseBuffer.slice(0, SEQUENCE_NUMBER_BYTES);
777
+ const sequenceNumber = getSequenceNumberFromByteArray(sequenceNumberVector);
778
+ if (sequenceNumber !== (lastKnownInboundSequenceNumber + 1)) {
779
+ throw new Error('Encrypted message has invalid sequence number');
780
+ }
781
+ lastKnownInboundSequenceNumber = sequenceNumber;
782
+ const jsonRpcMessage = await decryptJsonRpcMessage(responseBuffer, state.sharedSecret);
783
+ const responsePromise = jsonRpcResponsePromises[jsonRpcMessage.id];
784
+ delete jsonRpcResponsePromises[jsonRpcMessage.id];
785
+ responsePromise.resolve(jsonRpcMessage.result);
786
+ }
787
+ catch (e) {
788
+ if (e instanceof SolanaMobileWalletAdapterProtocolError) {
789
+ const responsePromise = jsonRpcResponsePromises[e.jsonRpcMessageId];
790
+ delete jsonRpcResponsePromises[e.jsonRpcMessageId];
791
+ responsePromise.reject(e);
792
+ }
793
+ else {
794
+ throw e;
795
+ }
796
+ }
797
+ break;
798
+ case 'hello_req_sent': {
799
+ // if we receive an APP_PING message (empty message), resend the HELLO_REQ (see above)
800
+ if (responseBuffer.byteLength === 0) {
801
+ const ecdhKeypair = await generateECDHKeypair();
802
+ socket.send(await createHelloReq(ecdhKeypair.publicKey, associationKeypair.privateKey));
803
+ state = {
804
+ __type: 'hello_req_sent',
805
+ associationPublicKey: associationKeypair.publicKey,
806
+ ecdhPrivateKey: ecdhKeypair.privateKey,
807
+ };
808
+ break;
809
+ }
810
+ const sharedSecret = await parseHelloRsp(responseBuffer, state.associationPublicKey, state.ecdhPrivateKey);
811
+ const sessionPropertiesBuffer = responseBuffer.slice(ENCODED_PUBLIC_KEY_LENGTH_BYTES);
812
+ const sessionProperties = sessionPropertiesBuffer.byteLength !== 0
813
+ ? await (async () => {
814
+ const sequenceNumberVector = sessionPropertiesBuffer.slice(0, SEQUENCE_NUMBER_BYTES);
815
+ const sequenceNumber = getSequenceNumberFromByteArray(sequenceNumberVector);
816
+ if (sequenceNumber !== (lastKnownInboundSequenceNumber + 1)) {
817
+ throw new Error('Encrypted message has invalid sequence number');
818
+ }
819
+ lastKnownInboundSequenceNumber = sequenceNumber;
820
+ return parseSessionProps(sessionPropertiesBuffer, sharedSecret);
821
+ })() : { protocol_version: 'legacy' };
822
+ state = { __type: 'connected', sharedSecret, sessionProperties };
823
+ const wallet = createMobileWalletProxy(sessionProperties.protocol_version, async (method, params) => {
824
+ const id = nextJsonRpcMessageId++;
825
+ socket.send(await encryptJsonRpcMessage({
826
+ id,
827
+ jsonrpc: '2.0',
828
+ method,
829
+ params: params ?? {},
830
+ }, sharedSecret));
831
+ return new Promise((resolve, reject) => {
832
+ jsonRpcResponsePromises[id] = {
833
+ resolve(result) {
834
+ switch (method) {
835
+ case 'authorize':
836
+ case 'reauthorize': {
837
+ const { wallet_uri_base } = result;
838
+ if (wallet_uri_base != null) {
839
+ try {
840
+ assertSecureEndpointSpecificURI(wallet_uri_base);
841
+ }
842
+ catch (e) {
843
+ reject(e);
844
+ return;
845
+ }
846
+ }
847
+ break;
848
+ }
849
+ }
850
+ resolve(result);
851
+ },
852
+ reject,
853
+ };
854
+ });
855
+ });
856
+ sessionEstablished = true;
857
+ try {
858
+ resolve(wallet);
859
+ }
860
+ catch (e) {
861
+ reject(e);
862
+ }
863
+ break;
864
+ }
865
+ }
866
+ };
867
+ handleForceClose = () => {
868
+ socket.removeEventListener('message', handleMessage);
869
+ disposeSocket();
870
+ if (!sessionEstablished) {
871
+ reject(new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_SESSION_CLOSED, `The wallet session was closed before connection.`, { closeEvent: new CloseEvent('socket was closed before connection') }));
872
+ }
873
+ };
874
+ let disposeSocket;
875
+ let retryWaitTimeoutId;
876
+ const attemptSocketConnection = () => {
877
+ if (disposeSocket) {
878
+ disposeSocket();
879
+ }
880
+ state = { __type: 'connecting', associationKeypair };
881
+ if (connectionStartTime === undefined) {
882
+ connectionStartTime = Date.now();
883
+ }
884
+ socket = new WebSocket(websocketURL, [WEBSOCKET_PROTOCOL_BINARY]);
885
+ socket.addEventListener('open', handleOpen);
886
+ socket.addEventListener('close', handleClose);
887
+ socket.addEventListener('error', handleError);
888
+ socket.addEventListener('message', handleMessage);
889
+ disposeSocket = () => {
890
+ window.clearTimeout(retryWaitTimeoutId);
891
+ socket.removeEventListener('open', handleOpen);
892
+ socket.removeEventListener('close', handleClose);
893
+ socket.removeEventListener('error', handleError);
894
+ socket.removeEventListener('message', handleMessage);
895
+ };
896
+ };
897
+ attemptSocketConnection();
898
+ }) };
899
+ }
900
+ async function startRemoteScenario$1(config) {
901
+ assertSecureContext();
902
+ const associationKeypair = await generateAssociationKeypair();
903
+ const websocketURL = `wss://${config?.remoteHostAuthority}/reflect`;
904
+ let connectionStartTime;
905
+ const getNextRetryDelayMs = (() => {
906
+ const schedule = [...WEBSOCKET_CONNECTION_CONFIG.retryDelayScheduleMs];
907
+ return () => (schedule.length > 1 ? schedule.shift() : schedule[0]);
908
+ })();
909
+ let nextJsonRpcMessageId = 1;
910
+ let lastKnownInboundSequenceNumber = 0;
911
+ let encoding;
912
+ let state = { __type: 'disconnected' };
913
+ let socket;
914
+ let disposeSocket;
915
+ let decodeBytes = async (evt) => {
916
+ if (encoding == 'base64') { // base64 encoding
917
+ const message = await evt.data;
918
+ return toUint8Array$1(message).buffer;
919
+ }
920
+ else {
921
+ return await evt.data.arrayBuffer();
922
+ }
923
+ };
924
+ // Reflector Connection Phase
925
+ // here we connect to the reflector and wait for the REFLECTOR_ID message
926
+ // so we build the association URL and return that back to the caller
927
+ const associationUrl = await new Promise((resolve, reject) => {
928
+ const handleOpen = async () => {
929
+ if (state.__type !== 'connecting') {
930
+ console.warn('Expected adapter state to be `connecting` at the moment the websocket opens. ' +
931
+ `Got \`${state.__type}\`.`);
932
+ return;
933
+ }
934
+ if (socket.protocol.includes(WEBSOCKET_PROTOCOL_BASE64)) {
935
+ encoding = 'base64';
936
+ }
937
+ else {
938
+ encoding = 'binary';
939
+ }
940
+ socket.removeEventListener('open', handleOpen);
941
+ };
942
+ const handleClose = (evt) => {
943
+ if (evt.wasClean) {
944
+ state = { __type: 'disconnected' };
945
+ }
946
+ else {
947
+ reject(new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_SESSION_CLOSED, `The wallet session dropped unexpectedly (${evt.code}: ${evt.reason}).`, { closeEvent: evt }));
948
+ }
949
+ disposeSocket();
950
+ };
951
+ const handleError = async (_evt) => {
952
+ disposeSocket();
953
+ if (Date.now() - connectionStartTime >= WEBSOCKET_CONNECTION_CONFIG.timeoutMs) {
954
+ reject(new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_SESSION_TIMEOUT, `Failed to connect to the wallet websocket at ${websocketURL}.`));
955
+ }
956
+ else {
957
+ await new Promise((resolve) => {
958
+ const retryDelayMs = getNextRetryDelayMs();
959
+ retryWaitTimeoutId = window.setTimeout(resolve, retryDelayMs);
960
+ });
961
+ attemptSocketConnection();
962
+ }
963
+ };
964
+ const handleReflectorIdMessage = async (evt) => {
965
+ const responseBuffer = await decodeBytes(evt);
966
+ if (state.__type === 'connecting') {
967
+ if (responseBuffer.byteLength == 0) {
968
+ throw new Error('Encountered unexpected message while connecting');
969
+ }
970
+ const reflectorId = getReflectorIdFromByteArray(responseBuffer);
971
+ state = {
972
+ __type: 'reflector_id_received',
973
+ reflectorId: reflectorId
974
+ };
975
+ const associationUrl = await getRemoteAssociateAndroidIntentURL(associationKeypair.publicKey, config.remoteHostAuthority, reflectorId, config?.baseUri);
976
+ socket.removeEventListener('message', handleReflectorIdMessage);
977
+ resolve(associationUrl);
978
+ }
979
+ };
980
+ let retryWaitTimeoutId;
981
+ const attemptSocketConnection = () => {
982
+ if (disposeSocket) {
983
+ disposeSocket();
984
+ }
985
+ state = { __type: 'connecting', associationKeypair };
986
+ if (connectionStartTime === undefined) {
987
+ connectionStartTime = Date.now();
988
+ }
989
+ socket = new WebSocket(websocketURL, [WEBSOCKET_PROTOCOL_BINARY, WEBSOCKET_PROTOCOL_BASE64]);
990
+ socket.addEventListener('open', handleOpen);
991
+ socket.addEventListener('close', handleClose);
992
+ socket.addEventListener('error', handleError);
993
+ socket.addEventListener('message', handleReflectorIdMessage);
994
+ disposeSocket = () => {
995
+ window.clearTimeout(retryWaitTimeoutId);
996
+ socket.removeEventListener('open', handleOpen);
997
+ socket.removeEventListener('close', handleClose);
998
+ socket.removeEventListener('error', handleError);
999
+ socket.removeEventListener('message', handleReflectorIdMessage);
1000
+ };
1001
+ };
1002
+ attemptSocketConnection();
1003
+ });
1004
+ // Wallet Connection Phase
1005
+ // here we return the association URL (containing the reflector ID) to the caller +
1006
+ // a promise that will resolve the MobileWallet object once the wallet connects.
1007
+ let sessionEstablished = false;
1008
+ let handleClose;
1009
+ return { associationUrl, close: () => {
1010
+ socket.close();
1011
+ handleClose();
1012
+ }, wallet: new Promise((resolve, reject) => {
1013
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1014
+ const jsonRpcResponsePromises = {};
1015
+ const handleMessage = async (evt) => {
1016
+ const responseBuffer = await decodeBytes(evt);
1017
+ switch (state.__type) {
1018
+ case 'reflector_id_received':
1019
+ if (responseBuffer.byteLength !== 0) {
1020
+ throw new Error('Encountered unexpected message while awaiting reflection');
1021
+ }
1022
+ const ecdhKeypair = await generateECDHKeypair();
1023
+ const binaryMsg = await createHelloReq(ecdhKeypair.publicKey, associationKeypair.privateKey);
1024
+ if (encoding == 'base64') {
1025
+ socket.send(fromUint8Array$1(binaryMsg));
1026
+ }
1027
+ else {
1028
+ socket.send(binaryMsg);
1029
+ }
1030
+ state = {
1031
+ __type: 'hello_req_sent',
1032
+ associationPublicKey: associationKeypair.publicKey,
1033
+ ecdhPrivateKey: ecdhKeypair.privateKey,
1034
+ };
1035
+ break;
1036
+ case 'connected':
1037
+ try {
1038
+ const sequenceNumberVector = responseBuffer.slice(0, SEQUENCE_NUMBER_BYTES);
1039
+ const sequenceNumber = getSequenceNumberFromByteArray(sequenceNumberVector);
1040
+ if (sequenceNumber !== (lastKnownInboundSequenceNumber + 1)) {
1041
+ throw new Error('Encrypted message has invalid sequence number');
1042
+ }
1043
+ lastKnownInboundSequenceNumber = sequenceNumber;
1044
+ const jsonRpcMessage = await decryptJsonRpcMessage(responseBuffer, state.sharedSecret);
1045
+ const responsePromise = jsonRpcResponsePromises[jsonRpcMessage.id];
1046
+ delete jsonRpcResponsePromises[jsonRpcMessage.id];
1047
+ responsePromise.resolve(jsonRpcMessage.result);
1048
+ }
1049
+ catch (e) {
1050
+ if (e instanceof SolanaMobileWalletAdapterProtocolError) {
1051
+ const responsePromise = jsonRpcResponsePromises[e.jsonRpcMessageId];
1052
+ delete jsonRpcResponsePromises[e.jsonRpcMessageId];
1053
+ responsePromise.reject(e);
1054
+ }
1055
+ else {
1056
+ throw e;
1057
+ }
1058
+ }
1059
+ break;
1060
+ case 'hello_req_sent': {
1061
+ const sharedSecret = await parseHelloRsp(responseBuffer, state.associationPublicKey, state.ecdhPrivateKey);
1062
+ const sessionPropertiesBuffer = responseBuffer.slice(ENCODED_PUBLIC_KEY_LENGTH_BYTES);
1063
+ const sessionProperties = sessionPropertiesBuffer.byteLength !== 0
1064
+ ? await (async () => {
1065
+ const sequenceNumberVector = sessionPropertiesBuffer.slice(0, SEQUENCE_NUMBER_BYTES);
1066
+ const sequenceNumber = getSequenceNumberFromByteArray(sequenceNumberVector);
1067
+ if (sequenceNumber !== (lastKnownInboundSequenceNumber + 1)) {
1068
+ throw new Error('Encrypted message has invalid sequence number');
1069
+ }
1070
+ lastKnownInboundSequenceNumber = sequenceNumber;
1071
+ return parseSessionProps(sessionPropertiesBuffer, sharedSecret);
1072
+ })() : { protocol_version: 'legacy' };
1073
+ state = { __type: 'connected', sharedSecret, sessionProperties };
1074
+ const wallet = createMobileWalletProxy(sessionProperties.protocol_version, async (method, params) => {
1075
+ const id = nextJsonRpcMessageId++;
1076
+ const binaryMsg = await encryptJsonRpcMessage({
1077
+ id,
1078
+ jsonrpc: '2.0',
1079
+ method,
1080
+ params: params ?? {},
1081
+ }, sharedSecret);
1082
+ if (encoding == 'base64') {
1083
+ socket.send(fromUint8Array$1(binaryMsg));
1084
+ }
1085
+ else {
1086
+ socket.send(binaryMsg);
1087
+ }
1088
+ return new Promise((resolve, reject) => {
1089
+ jsonRpcResponsePromises[id] = {
1090
+ resolve(result) {
1091
+ switch (method) {
1092
+ case 'authorize':
1093
+ case 'reauthorize': {
1094
+ const { wallet_uri_base } = result;
1095
+ if (wallet_uri_base != null) {
1096
+ try {
1097
+ assertSecureEndpointSpecificURI(wallet_uri_base);
1098
+ }
1099
+ catch (e) {
1100
+ reject(e);
1101
+ return;
1102
+ }
1103
+ }
1104
+ break;
1105
+ }
1106
+ }
1107
+ resolve(result);
1108
+ },
1109
+ reject,
1110
+ };
1111
+ });
1112
+ });
1113
+ sessionEstablished = true;
1114
+ try {
1115
+ resolve(wallet);
1116
+ }
1117
+ catch (e) {
1118
+ reject(e);
1119
+ }
1120
+ break;
1121
+ }
1122
+ }
1123
+ };
1124
+ socket.addEventListener('message', handleMessage);
1125
+ handleClose = () => {
1126
+ socket.removeEventListener('message', handleMessage);
1127
+ disposeSocket();
1128
+ if (!sessionEstablished) {
1129
+ reject(new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_SESSION_CLOSED, `The wallet session was closed before connection.`, { closeEvent: new CloseEvent('socket was closed before connection') }));
1130
+ }
1131
+ };
1132
+ }) };
1133
+ }
1134
+
1135
+ // base-x encoding / decoding
1136
+ // Copyright (c) 2018 base-x contributors
1137
+ // Copyright (c) 2014-2018 The Bitcoin Core developers (base58.cpp)
1138
+ // Distributed under the MIT software license, see the accompanying
1139
+ // file LICENSE or http://www.opensource.org/licenses/mit-license.php.
1140
+ function base (ALPHABET) {
1141
+ if (ALPHABET.length >= 255) { throw new TypeError('Alphabet too long') }
1142
+ const BASE_MAP = new Uint8Array(256);
1143
+ for (let j = 0; j < BASE_MAP.length; j++) {
1144
+ BASE_MAP[j] = 255;
1145
+ }
1146
+ for (let i = 0; i < ALPHABET.length; i++) {
1147
+ const x = ALPHABET.charAt(i);
1148
+ const xc = x.charCodeAt(0);
1149
+ if (BASE_MAP[xc] !== 255) { throw new TypeError(x + ' is ambiguous') }
1150
+ BASE_MAP[xc] = i;
1151
+ }
1152
+ const BASE = ALPHABET.length;
1153
+ const LEADER = ALPHABET.charAt(0);
1154
+ const FACTOR = Math.log(BASE) / Math.log(256); // log(BASE) / log(256), rounded up
1155
+ const iFACTOR = Math.log(256) / Math.log(BASE); // log(256) / log(BASE), rounded up
1156
+ function encode (source) {
1157
+ // eslint-disable-next-line no-empty
1158
+ if (source instanceof Uint8Array) ; else if (ArrayBuffer.isView(source)) {
1159
+ source = new Uint8Array(source.buffer, source.byteOffset, source.byteLength);
1160
+ } else if (Array.isArray(source)) {
1161
+ source = Uint8Array.from(source);
1162
+ }
1163
+ if (!(source instanceof Uint8Array)) { throw new TypeError('Expected Uint8Array') }
1164
+ if (source.length === 0) { return '' }
1165
+ // Skip & count leading zeroes.
1166
+ let zeroes = 0;
1167
+ let length = 0;
1168
+ let pbegin = 0;
1169
+ const pend = source.length;
1170
+ while (pbegin !== pend && source[pbegin] === 0) {
1171
+ pbegin++;
1172
+ zeroes++;
1173
+ }
1174
+ // Allocate enough space in big-endian base58 representation.
1175
+ const size = ((pend - pbegin) * iFACTOR + 1) >>> 0;
1176
+ const b58 = new Uint8Array(size);
1177
+ // Process the bytes.
1178
+ while (pbegin !== pend) {
1179
+ let carry = source[pbegin];
1180
+ // Apply "b58 = b58 * 256 + ch".
1181
+ let i = 0;
1182
+ for (let it1 = size - 1; (carry !== 0 || i < length) && (it1 !== -1); it1--, i++) {
1183
+ carry += (256 * b58[it1]) >>> 0;
1184
+ b58[it1] = (carry % BASE) >>> 0;
1185
+ carry = (carry / BASE) >>> 0;
1186
+ }
1187
+ if (carry !== 0) { throw new Error('Non-zero carry') }
1188
+ length = i;
1189
+ pbegin++;
1190
+ }
1191
+ // Skip leading zeroes in base58 result.
1192
+ let it2 = size - length;
1193
+ while (it2 !== size && b58[it2] === 0) {
1194
+ it2++;
1195
+ }
1196
+ // Translate the result into a string.
1197
+ let str = LEADER.repeat(zeroes);
1198
+ for (; it2 < size; ++it2) { str += ALPHABET.charAt(b58[it2]); }
1199
+ return str
1200
+ }
1201
+ function decodeUnsafe (source) {
1202
+ if (typeof source !== 'string') { throw new TypeError('Expected String') }
1203
+ if (source.length === 0) { return new Uint8Array() }
1204
+ let psz = 0;
1205
+ // Skip and count leading '1's.
1206
+ let zeroes = 0;
1207
+ let length = 0;
1208
+ while (source[psz] === LEADER) {
1209
+ zeroes++;
1210
+ psz++;
1211
+ }
1212
+ // Allocate enough space in big-endian base256 representation.
1213
+ const size = (((source.length - psz) * FACTOR) + 1) >>> 0; // log(58) / log(256), rounded up.
1214
+ const b256 = new Uint8Array(size);
1215
+ // Process the characters.
1216
+ while (psz < source.length) {
1217
+ // Find code of next character
1218
+ const charCode = source.charCodeAt(psz);
1219
+ // Base map can not be indexed using char code
1220
+ if (charCode > 255) { return }
1221
+ // Decode character
1222
+ let carry = BASE_MAP[charCode];
1223
+ // Invalid character
1224
+ if (carry === 255) { return }
1225
+ let i = 0;
1226
+ for (let it3 = size - 1; (carry !== 0 || i < length) && (it3 !== -1); it3--, i++) {
1227
+ carry += (BASE * b256[it3]) >>> 0;
1228
+ b256[it3] = (carry % 256) >>> 0;
1229
+ carry = (carry / 256) >>> 0;
1230
+ }
1231
+ if (carry !== 0) { throw new Error('Non-zero carry') }
1232
+ length = i;
1233
+ psz++;
1234
+ }
1235
+ // Skip leading zeroes in b256.
1236
+ let it4 = size - length;
1237
+ while (it4 !== size && b256[it4] === 0) {
1238
+ it4++;
1239
+ }
1240
+ const vch = new Uint8Array(zeroes + (size - it4));
1241
+ let j = zeroes;
1242
+ while (it4 !== size) {
1243
+ vch[j++] = b256[it4++];
1244
+ }
1245
+ return vch
1246
+ }
1247
+ function decode (string) {
1248
+ const buffer = decodeUnsafe(string);
1249
+ if (buffer) { return buffer }
1250
+ throw new Error('Non-base' + BASE + ' character')
1251
+ }
1252
+ return {
1253
+ encode,
1254
+ decodeUnsafe,
1255
+ decode
1256
+ }
1257
+ }
1258
+
1259
+ var ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
1260
+ var bs58 = base(ALPHABET);
1261
+
1262
+ function fromUint8Array(byteArray) {
1263
+ return window.btoa(String.fromCharCode.call(null, ...byteArray));
1264
+ }
1265
+ function toUint8Array(base64EncodedByteArray) {
1266
+ return new Uint8Array(window
1267
+ .atob(base64EncodedByteArray)
1268
+ .split('')
1269
+ .map((c) => c.charCodeAt(0)));
1270
+ }
1271
+
1272
+ function getPayloadFromTransaction(transaction) {
1273
+ const serializedTransaction = 'version' in transaction
1274
+ ? transaction.serialize()
1275
+ : transaction.serialize({
1276
+ requireAllSignatures: false,
1277
+ verifySignatures: false,
1278
+ });
1279
+ const payload = fromUint8Array(serializedTransaction);
1280
+ return payload;
1281
+ }
1282
+ function getTransactionFromWireMessage(byteArray) {
1283
+ const numSignatures = byteArray[0];
1284
+ const messageOffset = numSignatures * SIGNATURE_LENGTH_IN_BYTES + 1;
1285
+ const version = VersionedMessage.deserializeMessageVersion(byteArray.slice(messageOffset, byteArray.length));
1286
+ if (version === 'legacy') {
1287
+ return Transaction.from(byteArray);
1288
+ }
1289
+ else {
1290
+ return VersionedTransaction.deserialize(byteArray);
1291
+ }
1292
+ }
1293
+ async function transact(callback, config) {
1294
+ const augmentedCallback = (wallet) => {
1295
+ return callback(augmentWalletAPI(wallet));
1296
+ };
1297
+ return await transact$1(augmentedCallback, config);
1298
+ }
1299
+ async function startRemoteScenario(config) {
1300
+ const { wallet, close, associationUrl } = await startRemoteScenario$1(config);
1301
+ const augmentedPromise = wallet.then((wallet) => {
1302
+ return augmentWalletAPI(wallet);
1303
+ });
1304
+ return { wallet: augmentedPromise, close, associationUrl };
1305
+ }
1306
+ function augmentWalletAPI(wallet) {
1307
+ return new Proxy({}, {
1308
+ get(target, p) {
1309
+ if (target[p] == null) {
1310
+ switch (p) {
1311
+ case 'signAndSendTransactions':
1312
+ target[p] = async function ({ minContextSlot, commitment, skipPreflight, maxRetries, waitForCommitmentToSendNextTransaction, transactions, ...rest }) {
1313
+ const payloads = transactions.map(getPayloadFromTransaction);
1314
+ const options = {
1315
+ min_context_slot: minContextSlot,
1316
+ commitment: commitment,
1317
+ skip_preflight: skipPreflight,
1318
+ max_retries: maxRetries,
1319
+ wait_for_commitment_to_send_next_transaction: waitForCommitmentToSendNextTransaction
1320
+ };
1321
+ const { signatures: base64EncodedSignatures } = await wallet.signAndSendTransactions({
1322
+ ...rest,
1323
+ ...(Object.values(options).some(element => element != null)
1324
+ ? { options: options }
1325
+ : null),
1326
+ payloads,
1327
+ });
1328
+ const signatures = base64EncodedSignatures.map(toUint8Array).map(bs58.encode);
1329
+ return signatures;
1330
+ };
1331
+ break;
1332
+ case 'signMessages':
1333
+ target[p] = async function ({ payloads, ...rest }) {
1334
+ const base64EncodedPayloads = payloads.map(fromUint8Array);
1335
+ const { signed_payloads: base64EncodedSignedMessages } = await wallet.signMessages({
1336
+ ...rest,
1337
+ payloads: base64EncodedPayloads,
1338
+ });
1339
+ const signedMessages = base64EncodedSignedMessages.map(toUint8Array);
1340
+ return signedMessages;
1341
+ };
1342
+ break;
1343
+ case 'signTransactions':
1344
+ target[p] = async function ({ transactions, ...rest }) {
1345
+ const payloads = transactions.map(getPayloadFromTransaction);
1346
+ const { signed_payloads: base64EncodedCompiledTransactions } = await wallet.signTransactions({
1347
+ ...rest,
1348
+ payloads,
1349
+ });
1350
+ const compiledTransactions = base64EncodedCompiledTransactions.map(toUint8Array);
1351
+ const signedTransactions = compiledTransactions.map(getTransactionFromWireMessage);
1352
+ return signedTransactions;
1353
+ };
1354
+ break;
1355
+ default: {
1356
+ target[p] = wallet[p];
1357
+ break;
1358
+ }
1359
+ }
1360
+ }
1361
+ return target[p];
1362
+ },
1363
+ defineProperty() {
1364
+ return false;
1365
+ },
1366
+ deleteProperty() {
1367
+ return false;
1368
+ },
1369
+ });
1370
+ }
1371
+
1372
+ export { startRemoteScenario, transact };
1373
+ //# sourceMappingURL=index.browser-C9bFQZyQ.esm.js.map