@pooflabs/web 0.0.71 → 0.0.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{index-DFBVGK4A.js → index-CJuG7364.js} +2 -2
- package/dist/{index-DFBVGK4A.js.map → index-CJuG7364.js.map} +1 -1
- package/dist/{index-BpxspB4P.js → index-CMTlhmPS.js} +5 -4
- package/dist/{index-BpxspB4P.js.map → index-CMTlhmPS.js.map} +1 -1
- package/dist/{index-BEFBhDYr.esm.js → index-Crc3B1ZK.esm.js} +2 -2
- package/dist/{index-BEFBhDYr.esm.js.map → index-Crc3B1ZK.esm.js.map} +1 -1
- package/dist/{index-CIsuR4Ki.esm.js → index-u0Q8zhkj.esm.js} +5 -5
- package/dist/{index-CIsuR4Ki.esm.js.map → index-u0Q8zhkj.esm.js.map} +1 -1
- package/dist/index.browser-BOJRGZWX.js +1167 -0
- package/dist/index.browser-BOJRGZWX.js.map +1 -0
- package/dist/index.browser-C6e2ssNC.esm.js +4867 -0
- package/dist/index.browser-C6e2ssNC.esm.js.map +1 -0
- package/dist/index.browser-CPRwXOyN.js +311 -0
- package/dist/index.browser-CPRwXOyN.js.map +1 -0
- package/dist/index.browser-DZjyUgtx.esm.js +1164 -0
- package/dist/index.browser-DZjyUgtx.esm.js.map +1 -0
- package/dist/index.browser-DjDHEvuF.esm.js +308 -0
- package/dist/index.browser-DjDHEvuF.esm.js.map +1 -0
- package/dist/index.browser-jwxjZVpT.js +4877 -0
- package/dist/index.browser-jwxjZVpT.js.map +1 -0
- package/dist/index.esm.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +4 -15
|
@@ -0,0 +1,1164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TODO: docs
|
|
3
|
+
*/
|
|
4
|
+
function createSignInMessageText(input) {
|
|
5
|
+
// ${domain} wants you to sign in with your Solana account:
|
|
6
|
+
// ${address}
|
|
7
|
+
//
|
|
8
|
+
// ${statement}
|
|
9
|
+
//
|
|
10
|
+
// URI: ${uri}
|
|
11
|
+
// Version: ${version}
|
|
12
|
+
// Chain ID: ${chain}
|
|
13
|
+
// Nonce: ${nonce}
|
|
14
|
+
// Issued At: ${issued-at}
|
|
15
|
+
// Expiration Time: ${expiration-time}
|
|
16
|
+
// Not Before: ${not-before}
|
|
17
|
+
// Request ID: ${request-id}
|
|
18
|
+
// Resources:
|
|
19
|
+
// - ${resources[0]}
|
|
20
|
+
// - ${resources[1]}
|
|
21
|
+
// ...
|
|
22
|
+
// - ${resources[n]}
|
|
23
|
+
let message = `${input.domain} wants you to sign in with your Solana account:\n`;
|
|
24
|
+
message += `${input.address}`;
|
|
25
|
+
if (input.statement) {
|
|
26
|
+
message += `\n\n${input.statement}`;
|
|
27
|
+
}
|
|
28
|
+
const fields = [];
|
|
29
|
+
if (input.uri) {
|
|
30
|
+
fields.push(`URI: ${input.uri}`);
|
|
31
|
+
}
|
|
32
|
+
if (input.version) {
|
|
33
|
+
fields.push(`Version: ${input.version}`);
|
|
34
|
+
}
|
|
35
|
+
if (input.chainId) {
|
|
36
|
+
fields.push(`Chain ID: ${input.chainId}`);
|
|
37
|
+
}
|
|
38
|
+
if (input.nonce) {
|
|
39
|
+
fields.push(`Nonce: ${input.nonce}`);
|
|
40
|
+
}
|
|
41
|
+
if (input.issuedAt) {
|
|
42
|
+
fields.push(`Issued At: ${input.issuedAt}`);
|
|
43
|
+
}
|
|
44
|
+
if (input.expirationTime) {
|
|
45
|
+
fields.push(`Expiration Time: ${input.expirationTime}`);
|
|
46
|
+
}
|
|
47
|
+
if (input.notBefore) {
|
|
48
|
+
fields.push(`Not Before: ${input.notBefore}`);
|
|
49
|
+
}
|
|
50
|
+
if (input.requestId) {
|
|
51
|
+
fields.push(`Request ID: ${input.requestId}`);
|
|
52
|
+
}
|
|
53
|
+
if (input.resources) {
|
|
54
|
+
fields.push(`Resources:`);
|
|
55
|
+
for (const resource of input.resources) {
|
|
56
|
+
fields.push(`- ${resource}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (fields.length) {
|
|
60
|
+
message += `\n\n${fields.join('\n')}`;
|
|
61
|
+
}
|
|
62
|
+
return message;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function createDecoder(decoder) {
|
|
66
|
+
return Object.freeze({
|
|
67
|
+
...decoder,
|
|
68
|
+
decode: (bytes, offset = 0) => decoder.read(bytes, offset)[0]
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
var getBaseXDecoder = (alphabet4) => {
|
|
73
|
+
return createDecoder({
|
|
74
|
+
read(rawBytes, offset) {
|
|
75
|
+
const bytes = offset === 0 ? rawBytes : rawBytes.slice(offset);
|
|
76
|
+
if (bytes.length === 0) return ["", 0];
|
|
77
|
+
let trailIndex = bytes.findIndex((n) => n !== 0);
|
|
78
|
+
trailIndex = trailIndex === -1 ? bytes.length : trailIndex;
|
|
79
|
+
const leadingZeroes = alphabet4[0].repeat(trailIndex);
|
|
80
|
+
if (trailIndex === bytes.length) return [leadingZeroes, rawBytes.length];
|
|
81
|
+
const base10Number = bytes.slice(trailIndex).reduce((sum, byte) => sum * 256n + BigInt(byte), 0n);
|
|
82
|
+
const tailChars = getBaseXFromBigInt(base10Number, alphabet4);
|
|
83
|
+
return [leadingZeroes + tailChars, rawBytes.length];
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
function getBaseXFromBigInt(value, alphabet4) {
|
|
88
|
+
const base = BigInt(alphabet4.length);
|
|
89
|
+
const tailChars = [];
|
|
90
|
+
while (value > 0n) {
|
|
91
|
+
tailChars.unshift(alphabet4[Number(value % base)]);
|
|
92
|
+
value /= base;
|
|
93
|
+
}
|
|
94
|
+
return tailChars.join("");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/base58.ts
|
|
98
|
+
var alphabet2 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
99
|
+
var getBase58Decoder = () => getBaseXDecoder(alphabet2);
|
|
100
|
+
|
|
101
|
+
// Typescript `enums` thwart tree-shaking. See https://bargsten.org/jsts/enums/
|
|
102
|
+
const SolanaMobileWalletAdapterErrorCode = {
|
|
103
|
+
ERROR_ASSOCIATION_PORT_OUT_OF_RANGE: 'ERROR_ASSOCIATION_PORT_OUT_OF_RANGE',
|
|
104
|
+
ERROR_FORBIDDEN_WALLET_BASE_URL: 'ERROR_FORBIDDEN_WALLET_BASE_URL',
|
|
105
|
+
ERROR_SECURE_CONTEXT_REQUIRED: 'ERROR_SECURE_CONTEXT_REQUIRED',
|
|
106
|
+
ERROR_SESSION_CLOSED: 'ERROR_SESSION_CLOSED',
|
|
107
|
+
ERROR_SESSION_TIMEOUT: 'ERROR_SESSION_TIMEOUT',
|
|
108
|
+
ERROR_WALLET_NOT_FOUND: 'ERROR_WALLET_NOT_FOUND',
|
|
109
|
+
ERROR_INVALID_PROTOCOL_VERSION: 'ERROR_INVALID_PROTOCOL_VERSION'};
|
|
110
|
+
class SolanaMobileWalletAdapterError extends Error {
|
|
111
|
+
constructor(...args) {
|
|
112
|
+
const [code, message, data] = args;
|
|
113
|
+
super(message);
|
|
114
|
+
this.code = code;
|
|
115
|
+
this.data = data;
|
|
116
|
+
this.name = 'SolanaMobileWalletAdapterError';
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
class SolanaMobileWalletAdapterProtocolError extends Error {
|
|
120
|
+
constructor(...args) {
|
|
121
|
+
const [jsonRpcMessageId, code, message, data] = args;
|
|
122
|
+
super(message);
|
|
123
|
+
this.code = code;
|
|
124
|
+
this.data = data;
|
|
125
|
+
this.jsonRpcMessageId = jsonRpcMessageId;
|
|
126
|
+
this.name = 'SolanaMobileWalletAdapterProtocolError';
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/******************************************************************************
|
|
131
|
+
Copyright (c) Microsoft Corporation.
|
|
132
|
+
|
|
133
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
134
|
+
purpose with or without fee is hereby granted.
|
|
135
|
+
|
|
136
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
137
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
138
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
139
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
140
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
141
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
142
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
143
|
+
***************************************************************************** */
|
|
144
|
+
|
|
145
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
146
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
147
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
148
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
149
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
150
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
151
|
+
step((generator = generator.apply(thisArg, [])).next());
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function encode(input) {
|
|
156
|
+
return window.btoa(input);
|
|
157
|
+
}
|
|
158
|
+
function fromUint8Array$1(byteArray, urlsafe) {
|
|
159
|
+
const base64 = window.btoa(String.fromCharCode.call(null, ...byteArray));
|
|
160
|
+
if (urlsafe) {
|
|
161
|
+
return base64
|
|
162
|
+
.replace(/\+/g, '-')
|
|
163
|
+
.replace(/\//g, '_')
|
|
164
|
+
.replace(/=+$/, '');
|
|
165
|
+
}
|
|
166
|
+
else
|
|
167
|
+
return base64;
|
|
168
|
+
}
|
|
169
|
+
function toUint8Array(base64EncodedByteArray) {
|
|
170
|
+
return new Uint8Array(window
|
|
171
|
+
.atob(base64EncodedByteArray)
|
|
172
|
+
.split('')
|
|
173
|
+
.map((c) => c.charCodeAt(0)));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function createHelloReq(ecdhPublicKey, associationKeypairPrivateKey) {
|
|
177
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
178
|
+
const publicKeyBuffer = yield crypto.subtle.exportKey('raw', ecdhPublicKey);
|
|
179
|
+
const signatureBuffer = yield crypto.subtle.sign({ hash: 'SHA-256', name: 'ECDSA' }, associationKeypairPrivateKey, publicKeyBuffer);
|
|
180
|
+
const response = new Uint8Array(publicKeyBuffer.byteLength + signatureBuffer.byteLength);
|
|
181
|
+
response.set(new Uint8Array(publicKeyBuffer), 0);
|
|
182
|
+
response.set(new Uint8Array(signatureBuffer), publicKeyBuffer.byteLength);
|
|
183
|
+
return response;
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function createSIWSMessage(payload) {
|
|
188
|
+
return createSignInMessageText(payload);
|
|
189
|
+
}
|
|
190
|
+
function createSIWSMessageBase64Url(payload) {
|
|
191
|
+
return encode(createSIWSMessage(payload))
|
|
192
|
+
.replace(/\+/g, '-')
|
|
193
|
+
.replace(/\//g, '_')
|
|
194
|
+
.replace(/=+$/, ''); // convert to base64url encoding;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// optional features
|
|
198
|
+
const SolanaSignTransactions = 'solana:signTransactions';
|
|
199
|
+
const SolanaCloneAuthorization = 'solana:cloneAuthorization';
|
|
200
|
+
|
|
201
|
+
function fromUint8Array(byteArray) {
|
|
202
|
+
return getBase58Decoder().decode(byteArray);
|
|
203
|
+
}
|
|
204
|
+
function base64ToBase58(base64EncodedString) {
|
|
205
|
+
return fromUint8Array(toUint8Array(base64EncodedString));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Creates a {@link MobileWallet} proxy that handles backwards compatibility and API to RPC conversion.
|
|
210
|
+
*
|
|
211
|
+
* @param protocolVersion the protocol version in use for this session/request
|
|
212
|
+
* @param protocolRequestHandler callback function that handles sending the RPC request to the wallet endpoint.
|
|
213
|
+
* @returns a {@link MobileWallet} proxy
|
|
214
|
+
*/
|
|
215
|
+
function createMobileWalletProxy(protocolVersion, protocolRequestHandler) {
|
|
216
|
+
return new Proxy({}, {
|
|
217
|
+
get(target, p) {
|
|
218
|
+
// Wrapping a Proxy in a promise results in the Proxy being asked for a 'then' property so must
|
|
219
|
+
// return null if 'then' is called on this proxy to let the 'resolve()' call know this is not a promise.
|
|
220
|
+
// see: https://stackoverflow.com/a/53890904
|
|
221
|
+
//@ts-ignore
|
|
222
|
+
if (p === 'then') {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
if (target[p] == null) {
|
|
226
|
+
target[p] = function (inputParams) {
|
|
227
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
228
|
+
const { method, params } = handleMobileWalletRequest(p, inputParams, protocolVersion);
|
|
229
|
+
const result = yield protocolRequestHandler(method, params);
|
|
230
|
+
// if the request tried to sign in but the wallet did not return a sign in result, fallback on message signing
|
|
231
|
+
if (method === 'authorize' && params.sign_in_payload && !result.sign_in_result) {
|
|
232
|
+
result['sign_in_result'] = yield signInFallback(params.sign_in_payload, result, protocolRequestHandler);
|
|
233
|
+
}
|
|
234
|
+
return handleMobileWalletResponse(p, result, protocolVersion);
|
|
235
|
+
});
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
return target[p];
|
|
239
|
+
},
|
|
240
|
+
defineProperty() {
|
|
241
|
+
return false;
|
|
242
|
+
},
|
|
243
|
+
deleteProperty() {
|
|
244
|
+
return false;
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Handles all {@link MobileWallet} API requests and determines the correct MWA RPC method and params to call.
|
|
250
|
+
* This handles backwards compatibility, based on the provided @protocolVersion.
|
|
251
|
+
*
|
|
252
|
+
* @param methodName the name of {@link MobileWallet} method that was called
|
|
253
|
+
* @param methodParams the parameters that were passed to the method
|
|
254
|
+
* @param protocolVersion the protocol version in use for this session/request
|
|
255
|
+
* @returns the RPC request method and params that should be sent to the wallet endpoint
|
|
256
|
+
*/
|
|
257
|
+
function handleMobileWalletRequest(methodName, methodParams, protocolVersion) {
|
|
258
|
+
let params = methodParams;
|
|
259
|
+
let method = methodName
|
|
260
|
+
.toString()
|
|
261
|
+
.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)
|
|
262
|
+
.toLowerCase();
|
|
263
|
+
switch (methodName) {
|
|
264
|
+
case 'authorize': {
|
|
265
|
+
let { chain } = params;
|
|
266
|
+
if (protocolVersion === 'legacy') {
|
|
267
|
+
switch (chain) {
|
|
268
|
+
case 'solana:testnet': {
|
|
269
|
+
chain = 'testnet';
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
case 'solana:devnet': {
|
|
273
|
+
chain = 'devnet';
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
case 'solana:mainnet': {
|
|
277
|
+
chain = 'mainnet-beta';
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
default: {
|
|
281
|
+
chain = params.cluster;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
params.cluster = chain;
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
switch (chain) {
|
|
288
|
+
case 'testnet':
|
|
289
|
+
case 'devnet': {
|
|
290
|
+
chain = `solana:${chain}`;
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
case 'mainnet-beta': {
|
|
294
|
+
chain = 'solana:mainnet';
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
params.chain = chain;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
case 'reauthorize': {
|
|
302
|
+
const { auth_token, identity } = params;
|
|
303
|
+
if (auth_token) {
|
|
304
|
+
switch (protocolVersion) {
|
|
305
|
+
case 'legacy': {
|
|
306
|
+
method = 'reauthorize';
|
|
307
|
+
params = { auth_token: auth_token, identity: identity };
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
default: {
|
|
311
|
+
method = 'authorize';
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return { method, params };
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Handles all {@link MobileWallet} API responses and modifies the response for backwards compatibility, if needed
|
|
323
|
+
*
|
|
324
|
+
* @param method the {@link MobileWallet} method that was called
|
|
325
|
+
* @param response the original response that was returned by the method call
|
|
326
|
+
* @param protocolVersion the protocol version in use for this session/request
|
|
327
|
+
* @returns the possibly modified response
|
|
328
|
+
*/
|
|
329
|
+
function handleMobileWalletResponse(method, response, protocolVersion) {
|
|
330
|
+
switch (method) {
|
|
331
|
+
case 'getCapabilities': {
|
|
332
|
+
const capabilities = response;
|
|
333
|
+
switch (protocolVersion) {
|
|
334
|
+
case 'legacy': {
|
|
335
|
+
const features = [SolanaSignTransactions];
|
|
336
|
+
if (capabilities.supports_clone_authorization === true) {
|
|
337
|
+
features.push(SolanaCloneAuthorization);
|
|
338
|
+
}
|
|
339
|
+
return Object.assign(Object.assign({}, capabilities), { features: features });
|
|
340
|
+
}
|
|
341
|
+
case 'v1': {
|
|
342
|
+
return Object.assign(Object.assign({}, capabilities), { supports_sign_and_send_transactions: true, supports_clone_authorization: capabilities.features.includes(SolanaCloneAuthorization) });
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return response;
|
|
348
|
+
}
|
|
349
|
+
function signInFallback(signInPayload, authorizationResult, protocolRequestHandler) {
|
|
350
|
+
var _a;
|
|
351
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
352
|
+
const domain = (_a = signInPayload.domain) !== null && _a !== void 0 ? _a : window.location.host;
|
|
353
|
+
const address = authorizationResult.accounts[0].address;
|
|
354
|
+
const siwsMessage = createSIWSMessageBase64Url(Object.assign(Object.assign({}, signInPayload), { domain, address: base64ToBase58(address) }));
|
|
355
|
+
const signMessageResult = yield protocolRequestHandler('sign_messages', {
|
|
356
|
+
addresses: [address],
|
|
357
|
+
payloads: [siwsMessage]
|
|
358
|
+
});
|
|
359
|
+
const signedPayload = toUint8Array(signMessageResult.signed_payloads[0]);
|
|
360
|
+
const signedMessage = fromUint8Array$1(signedPayload.slice(0, signedPayload.length - 64));
|
|
361
|
+
const signature = fromUint8Array$1(signedPayload.slice(signedPayload.length - 64));
|
|
362
|
+
const signInResult = {
|
|
363
|
+
address: address,
|
|
364
|
+
// Workaround: some wallets have been observed to only reply with the message signature.
|
|
365
|
+
// This is non-compliant with the spec, but in the interest of maximizing compatibility,
|
|
366
|
+
// detect this case and reuse the original message.
|
|
367
|
+
signed_message: signedMessage.length == 0 ? siwsMessage : signedMessage,
|
|
368
|
+
signature
|
|
369
|
+
};
|
|
370
|
+
return signInResult;
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const SEQUENCE_NUMBER_BYTES = 4;
|
|
375
|
+
function createSequenceNumberVector(sequenceNumber) {
|
|
376
|
+
if (sequenceNumber >= 4294967296) {
|
|
377
|
+
throw new Error('Outbound sequence number overflow. The maximum sequence number is 32-bytes.');
|
|
378
|
+
}
|
|
379
|
+
const byteArray = new ArrayBuffer(SEQUENCE_NUMBER_BYTES);
|
|
380
|
+
const view = new DataView(byteArray);
|
|
381
|
+
view.setUint32(0, sequenceNumber, /* littleEndian */ false);
|
|
382
|
+
return new Uint8Array(byteArray);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const INITIALIZATION_VECTOR_BYTES = 12;
|
|
386
|
+
const ENCODED_PUBLIC_KEY_LENGTH_BYTES = 65;
|
|
387
|
+
function encryptMessage(plaintext, sequenceNumber, sharedSecret) {
|
|
388
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
389
|
+
const sequenceNumberVector = createSequenceNumberVector(sequenceNumber);
|
|
390
|
+
const initializationVector = new Uint8Array(INITIALIZATION_VECTOR_BYTES);
|
|
391
|
+
crypto.getRandomValues(initializationVector);
|
|
392
|
+
const ciphertext = yield crypto.subtle.encrypt(getAlgorithmParams(sequenceNumberVector, initializationVector), sharedSecret, new TextEncoder().encode(plaintext));
|
|
393
|
+
const response = new Uint8Array(sequenceNumberVector.byteLength + initializationVector.byteLength + ciphertext.byteLength);
|
|
394
|
+
response.set(new Uint8Array(sequenceNumberVector), 0);
|
|
395
|
+
response.set(new Uint8Array(initializationVector), sequenceNumberVector.byteLength);
|
|
396
|
+
response.set(new Uint8Array(ciphertext), sequenceNumberVector.byteLength + initializationVector.byteLength);
|
|
397
|
+
return response;
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
function decryptMessage(message, sharedSecret) {
|
|
401
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
402
|
+
const sequenceNumberVector = message.slice(0, SEQUENCE_NUMBER_BYTES);
|
|
403
|
+
const initializationVector = message.slice(SEQUENCE_NUMBER_BYTES, SEQUENCE_NUMBER_BYTES + INITIALIZATION_VECTOR_BYTES);
|
|
404
|
+
const ciphertext = message.slice(SEQUENCE_NUMBER_BYTES + INITIALIZATION_VECTOR_BYTES);
|
|
405
|
+
const plaintextBuffer = yield crypto.subtle.decrypt(getAlgorithmParams(sequenceNumberVector, initializationVector), sharedSecret, ciphertext);
|
|
406
|
+
const plaintext = getUtf8Decoder().decode(plaintextBuffer);
|
|
407
|
+
return plaintext;
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
function getAlgorithmParams(sequenceNumber, initializationVector) {
|
|
411
|
+
return {
|
|
412
|
+
additionalData: sequenceNumber,
|
|
413
|
+
iv: initializationVector,
|
|
414
|
+
name: 'AES-GCM',
|
|
415
|
+
tagLength: 128, // 16 byte tag => 128 bits
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
let _utf8Decoder;
|
|
419
|
+
function getUtf8Decoder() {
|
|
420
|
+
if (_utf8Decoder === undefined) {
|
|
421
|
+
_utf8Decoder = new TextDecoder('utf-8');
|
|
422
|
+
}
|
|
423
|
+
return _utf8Decoder;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function generateAssociationKeypair() {
|
|
427
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
428
|
+
return yield crypto.subtle.generateKey({
|
|
429
|
+
name: 'ECDSA',
|
|
430
|
+
namedCurve: 'P-256',
|
|
431
|
+
}, false /* extractable */, ['sign'] /* keyUsages */);
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function generateECDHKeypair() {
|
|
436
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
437
|
+
return yield crypto.subtle.generateKey({
|
|
438
|
+
name: 'ECDH',
|
|
439
|
+
namedCurve: 'P-256',
|
|
440
|
+
}, false /* extractable */, ['deriveKey', 'deriveBits'] /* keyUsages */);
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// https://stackoverflow.com/a/9458996/802047
|
|
445
|
+
function arrayBufferToBase64String(buffer) {
|
|
446
|
+
let binary = '';
|
|
447
|
+
const bytes = new Uint8Array(buffer);
|
|
448
|
+
const len = bytes.byteLength;
|
|
449
|
+
for (let ii = 0; ii < len; ii++) {
|
|
450
|
+
binary += String.fromCharCode(bytes[ii]);
|
|
451
|
+
}
|
|
452
|
+
return window.btoa(binary);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function getRandomAssociationPort() {
|
|
456
|
+
return assertAssociationPort(49152 + Math.floor(Math.random() * (65535 - 49152 + 1)));
|
|
457
|
+
}
|
|
458
|
+
function assertAssociationPort(port) {
|
|
459
|
+
if (port < 49152 || port > 65535) {
|
|
460
|
+
throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_ASSOCIATION_PORT_OUT_OF_RANGE, `Association port number must be between 49152 and 65535. ${port} given.`, { port });
|
|
461
|
+
}
|
|
462
|
+
return port;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function getStringWithURLUnsafeCharactersReplaced(unsafeBase64EncodedString) {
|
|
466
|
+
return unsafeBase64EncodedString.replace(/[/+=]/g, (m) => ({
|
|
467
|
+
'/': '_',
|
|
468
|
+
'+': '-',
|
|
469
|
+
'=': '.',
|
|
470
|
+
}[m]));
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const INTENT_NAME = 'solana-wallet';
|
|
474
|
+
function getPathParts(pathString) {
|
|
475
|
+
return (pathString
|
|
476
|
+
// Strip leading and trailing slashes
|
|
477
|
+
.replace(/(^\/+|\/+$)/g, '')
|
|
478
|
+
// Return an array of directories
|
|
479
|
+
.split('/'));
|
|
480
|
+
}
|
|
481
|
+
function getIntentURL(methodPathname, intentUrlBase) {
|
|
482
|
+
let baseUrl = null;
|
|
483
|
+
if (intentUrlBase) {
|
|
484
|
+
try {
|
|
485
|
+
baseUrl = new URL(intentUrlBase);
|
|
486
|
+
}
|
|
487
|
+
catch (_a) { } // eslint-disable-line no-empty
|
|
488
|
+
if ((baseUrl === null || baseUrl === void 0 ? void 0 : baseUrl.protocol) !== 'https:') {
|
|
489
|
+
throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_FORBIDDEN_WALLET_BASE_URL, 'Base URLs supplied by wallets must be valid `https` URLs');
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
baseUrl || (baseUrl = new URL(`${INTENT_NAME}:/`));
|
|
493
|
+
const pathname = methodPathname.startsWith('/')
|
|
494
|
+
? // Method is an absolute path. Replace it wholesale.
|
|
495
|
+
methodPathname
|
|
496
|
+
: // Method is a relative path. Merge it with the existing one.
|
|
497
|
+
[...getPathParts(baseUrl.pathname), ...getPathParts(methodPathname)].join('/');
|
|
498
|
+
return new URL(pathname, baseUrl);
|
|
499
|
+
}
|
|
500
|
+
function getAssociateAndroidIntentURL(associationPublicKey, putativePort, associationURLBase, protocolVersions = ['v1']) {
|
|
501
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
502
|
+
const associationPort = assertAssociationPort(putativePort);
|
|
503
|
+
const exportedKey = yield crypto.subtle.exportKey('raw', associationPublicKey);
|
|
504
|
+
const encodedKey = arrayBufferToBase64String(exportedKey);
|
|
505
|
+
const url = getIntentURL('v1/associate/local', associationURLBase);
|
|
506
|
+
url.searchParams.set('association', getStringWithURLUnsafeCharactersReplaced(encodedKey));
|
|
507
|
+
url.searchParams.set('port', `${associationPort}`);
|
|
508
|
+
protocolVersions.forEach((version) => {
|
|
509
|
+
url.searchParams.set('v', version);
|
|
510
|
+
});
|
|
511
|
+
return url;
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
function getRemoteAssociateAndroidIntentURL(associationPublicKey, hostAuthority, reflectorId, associationURLBase, protocolVersions = ['v1']) {
|
|
515
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
516
|
+
const exportedKey = yield crypto.subtle.exportKey('raw', associationPublicKey);
|
|
517
|
+
const encodedKey = arrayBufferToBase64String(exportedKey);
|
|
518
|
+
const url = getIntentURL('v1/associate/remote', associationURLBase);
|
|
519
|
+
url.searchParams.set('association', getStringWithURLUnsafeCharactersReplaced(encodedKey));
|
|
520
|
+
url.searchParams.set('reflector', `${hostAuthority}`);
|
|
521
|
+
url.searchParams.set('id', `${fromUint8Array$1(reflectorId, true)}`);
|
|
522
|
+
protocolVersions.forEach((version) => {
|
|
523
|
+
url.searchParams.set('v', version);
|
|
524
|
+
});
|
|
525
|
+
return url;
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function encryptJsonRpcMessage(jsonRpcMessage, sharedSecret) {
|
|
530
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
531
|
+
const plaintext = JSON.stringify(jsonRpcMessage);
|
|
532
|
+
const sequenceNumber = jsonRpcMessage.id;
|
|
533
|
+
return encryptMessage(plaintext, sequenceNumber, sharedSecret);
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
function decryptJsonRpcMessage(message, sharedSecret) {
|
|
537
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
538
|
+
const plaintext = yield decryptMessage(message, sharedSecret);
|
|
539
|
+
const jsonRpcMessage = JSON.parse(plaintext);
|
|
540
|
+
if (Object.hasOwnProperty.call(jsonRpcMessage, 'error')) {
|
|
541
|
+
throw new SolanaMobileWalletAdapterProtocolError(jsonRpcMessage.id, jsonRpcMessage.error.code, jsonRpcMessage.error.message);
|
|
542
|
+
}
|
|
543
|
+
return jsonRpcMessage;
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function parseHelloRsp(payloadBuffer, // The X9.62-encoded wallet endpoint ephemeral ECDH public keypoint.
|
|
548
|
+
associationPublicKey, ecdhPrivateKey) {
|
|
549
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
550
|
+
const [associationPublicKeyBuffer, walletPublicKey] = yield Promise.all([
|
|
551
|
+
crypto.subtle.exportKey('raw', associationPublicKey),
|
|
552
|
+
crypto.subtle.importKey('raw', payloadBuffer.slice(0, ENCODED_PUBLIC_KEY_LENGTH_BYTES), { name: 'ECDH', namedCurve: 'P-256' }, false /* extractable */, [] /* keyUsages */),
|
|
553
|
+
]);
|
|
554
|
+
const sharedSecret = yield crypto.subtle.deriveBits({ name: 'ECDH', public: walletPublicKey }, ecdhPrivateKey, 256);
|
|
555
|
+
const ecdhSecretKey = yield crypto.subtle.importKey('raw', sharedSecret, 'HKDF', false /* extractable */, ['deriveKey'] /* keyUsages */);
|
|
556
|
+
const aesKeyMaterialVal = yield crypto.subtle.deriveKey({
|
|
557
|
+
name: 'HKDF',
|
|
558
|
+
hash: 'SHA-256',
|
|
559
|
+
salt: new Uint8Array(associationPublicKeyBuffer),
|
|
560
|
+
info: new Uint8Array(),
|
|
561
|
+
}, ecdhSecretKey, { name: 'AES-GCM', length: 128 }, false /* extractable */, ['encrypt', 'decrypt']);
|
|
562
|
+
return aesKeyMaterialVal;
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function parseSessionProps(message, sharedSecret) {
|
|
567
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
568
|
+
const plaintext = yield decryptMessage(message, sharedSecret);
|
|
569
|
+
const jsonProperties = JSON.parse(plaintext);
|
|
570
|
+
let protocolVersion = 'legacy';
|
|
571
|
+
if (Object.hasOwnProperty.call(jsonProperties, 'v')) {
|
|
572
|
+
switch (jsonProperties.v) {
|
|
573
|
+
case 1:
|
|
574
|
+
case '1':
|
|
575
|
+
case 'v1':
|
|
576
|
+
protocolVersion = 'v1';
|
|
577
|
+
break;
|
|
578
|
+
case 'legacy':
|
|
579
|
+
protocolVersion = 'legacy';
|
|
580
|
+
break;
|
|
581
|
+
default:
|
|
582
|
+
throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_INVALID_PROTOCOL_VERSION, `Unknown/unsupported protocol version: ${jsonProperties.v}`);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
return ({
|
|
586
|
+
protocol_version: protocolVersion
|
|
587
|
+
});
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// Typescript `enums` thwart tree-shaking. See https://bargsten.org/jsts/enums/
|
|
592
|
+
const Browser = {
|
|
593
|
+
Firefox: 0,
|
|
594
|
+
Other: 1,
|
|
595
|
+
};
|
|
596
|
+
function assertUnreachable(x) {
|
|
597
|
+
return x;
|
|
598
|
+
}
|
|
599
|
+
function getBrowser() {
|
|
600
|
+
return navigator.userAgent.indexOf('Firefox/') !== -1 ? Browser.Firefox : Browser.Other;
|
|
601
|
+
}
|
|
602
|
+
function getDetectionPromise() {
|
|
603
|
+
// Chrome and others silently fail if a custom protocol is not supported.
|
|
604
|
+
// For these, we wait to see if the browser is navigated away from in
|
|
605
|
+
// a reasonable amount of time (ie. the native wallet opened).
|
|
606
|
+
return new Promise((resolve, reject) => {
|
|
607
|
+
function cleanup() {
|
|
608
|
+
clearTimeout(timeoutId);
|
|
609
|
+
window.removeEventListener('blur', handleBlur);
|
|
610
|
+
}
|
|
611
|
+
function handleBlur() {
|
|
612
|
+
cleanup();
|
|
613
|
+
resolve();
|
|
614
|
+
}
|
|
615
|
+
window.addEventListener('blur', handleBlur);
|
|
616
|
+
const timeoutId = setTimeout(() => {
|
|
617
|
+
cleanup();
|
|
618
|
+
reject();
|
|
619
|
+
}, 3000);
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
let _frame = null;
|
|
623
|
+
function launchUrlThroughHiddenFrame(url) {
|
|
624
|
+
if (_frame == null) {
|
|
625
|
+
_frame = document.createElement('iframe');
|
|
626
|
+
_frame.style.display = 'none';
|
|
627
|
+
document.body.appendChild(_frame);
|
|
628
|
+
}
|
|
629
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
630
|
+
_frame.contentWindow.location.href = url.toString();
|
|
631
|
+
}
|
|
632
|
+
function launchAssociation(associationUrl) {
|
|
633
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
634
|
+
if (associationUrl.protocol === 'https:') {
|
|
635
|
+
// The association URL is an Android 'App Link' or iOS 'Universal Link'.
|
|
636
|
+
// These are regular web URLs that are designed to launch an app if it
|
|
637
|
+
// is installed or load the actual target webpage if not.
|
|
638
|
+
window.location.assign(associationUrl);
|
|
639
|
+
}
|
|
640
|
+
else {
|
|
641
|
+
// The association URL has a custom protocol (eg. `solana-wallet:`)
|
|
642
|
+
try {
|
|
643
|
+
const browser = getBrowser();
|
|
644
|
+
switch (browser) {
|
|
645
|
+
case Browser.Firefox:
|
|
646
|
+
// If a custom protocol is not supported in Firefox, it throws.
|
|
647
|
+
launchUrlThroughHiddenFrame(associationUrl);
|
|
648
|
+
// If we reached this line, it's supported.
|
|
649
|
+
break;
|
|
650
|
+
case Browser.Other: {
|
|
651
|
+
const detectionPromise = getDetectionPromise();
|
|
652
|
+
window.location.assign(associationUrl);
|
|
653
|
+
yield detectionPromise;
|
|
654
|
+
break;
|
|
655
|
+
}
|
|
656
|
+
default:
|
|
657
|
+
assertUnreachable(browser);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
catch (e) {
|
|
661
|
+
throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_WALLET_NOT_FOUND, 'Found no installed wallet that supports the mobile wallet protocol.');
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
function startSession(associationPublicKey, associationURLBase) {
|
|
667
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
668
|
+
const randomAssociationPort = getRandomAssociationPort();
|
|
669
|
+
const associationUrl = yield getAssociateAndroidIntentURL(associationPublicKey, randomAssociationPort, associationURLBase);
|
|
670
|
+
yield launchAssociation(associationUrl);
|
|
671
|
+
return randomAssociationPort;
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
const WEBSOCKET_CONNECTION_CONFIG = {
|
|
676
|
+
/**
|
|
677
|
+
* 300 milliseconds is a generally accepted threshold for what someone
|
|
678
|
+
* would consider an acceptable response time for a user interface
|
|
679
|
+
* after having performed a low-attention tapping task. We set the initial
|
|
680
|
+
* interval at which we wait for the wallet to set up the websocket at
|
|
681
|
+
* half this, as per the Nyquist frequency, with a progressive backoff
|
|
682
|
+
* sequence from there. The total wait time is 30s, which allows for the
|
|
683
|
+
* user to be presented with a disambiguation dialog, select a wallet, and
|
|
684
|
+
* for the wallet app to subsequently start.
|
|
685
|
+
*/
|
|
686
|
+
retryDelayScheduleMs: [150, 150, 200, 500, 500, 750, 750, 1000],
|
|
687
|
+
timeoutMs: 30000,
|
|
688
|
+
};
|
|
689
|
+
const WEBSOCKET_PROTOCOL_BINARY = 'com.solana.mobilewalletadapter.v1';
|
|
690
|
+
const WEBSOCKET_PROTOCOL_BASE64 = 'com.solana.mobilewalletadapter.v1.base64';
|
|
691
|
+
function assertSecureContext() {
|
|
692
|
+
if (typeof window === 'undefined' || window.isSecureContext !== true) {
|
|
693
|
+
throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_SECURE_CONTEXT_REQUIRED, 'The mobile wallet adapter protocol must be used in a secure context (`https`).');
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
function assertSecureEndpointSpecificURI(walletUriBase) {
|
|
697
|
+
let url;
|
|
698
|
+
try {
|
|
699
|
+
url = new URL(walletUriBase);
|
|
700
|
+
}
|
|
701
|
+
catch (_a) {
|
|
702
|
+
throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_FORBIDDEN_WALLET_BASE_URL, 'Invalid base URL supplied by wallet');
|
|
703
|
+
}
|
|
704
|
+
if (url.protocol !== 'https:') {
|
|
705
|
+
throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_FORBIDDEN_WALLET_BASE_URL, 'Base URLs supplied by wallets must be valid `https` URLs');
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
function getSequenceNumberFromByteArray(byteArray) {
|
|
709
|
+
const view = new DataView(byteArray);
|
|
710
|
+
return view.getUint32(0, /* littleEndian */ false);
|
|
711
|
+
}
|
|
712
|
+
function decodeVarLong(byteArray) {
|
|
713
|
+
var bytes = new Uint8Array(byteArray), l = byteArray.byteLength, limit = 10, value = 0, offset = 0, b;
|
|
714
|
+
do {
|
|
715
|
+
if (offset >= l || offset > limit)
|
|
716
|
+
throw new RangeError('Failed to decode varint');
|
|
717
|
+
b = bytes[offset++];
|
|
718
|
+
value |= (b & 0x7F) << (7 * offset);
|
|
719
|
+
} while (b >= 0x80);
|
|
720
|
+
return { value, offset };
|
|
721
|
+
}
|
|
722
|
+
function getReflectorIdFromByteArray(byteArray) {
|
|
723
|
+
let { value: length, offset } = decodeVarLong(byteArray);
|
|
724
|
+
return new Uint8Array(byteArray.slice(offset, offset + length));
|
|
725
|
+
}
|
|
726
|
+
function transact(callback, config) {
|
|
727
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
728
|
+
assertSecureContext();
|
|
729
|
+
const associationKeypair = yield generateAssociationKeypair();
|
|
730
|
+
const sessionPort = yield startSession(associationKeypair.publicKey, config === null || config === void 0 ? void 0 : config.baseUri);
|
|
731
|
+
const websocketURL = `ws://localhost:${sessionPort}/solana-wallet`;
|
|
732
|
+
let connectionStartTime;
|
|
733
|
+
const getNextRetryDelayMs = (() => {
|
|
734
|
+
const schedule = [...WEBSOCKET_CONNECTION_CONFIG.retryDelayScheduleMs];
|
|
735
|
+
return () => (schedule.length > 1 ? schedule.shift() : schedule[0]);
|
|
736
|
+
})();
|
|
737
|
+
let nextJsonRpcMessageId = 1;
|
|
738
|
+
let lastKnownInboundSequenceNumber = 0;
|
|
739
|
+
let state = { __type: 'disconnected' };
|
|
740
|
+
return new Promise((resolve, reject) => {
|
|
741
|
+
let socket;
|
|
742
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
743
|
+
const jsonRpcResponsePromises = {};
|
|
744
|
+
const handleOpen = () => __awaiter(this, void 0, void 0, function* () {
|
|
745
|
+
if (state.__type !== 'connecting') {
|
|
746
|
+
console.warn('Expected adapter state to be `connecting` at the moment the websocket opens. ' +
|
|
747
|
+
`Got \`${state.__type}\`.`);
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
socket.removeEventListener('open', handleOpen);
|
|
751
|
+
// previous versions of this library and walletlib incorrectly implemented the MWA session
|
|
752
|
+
// establishment protocol for local connections. The dapp is supposed to wait for the
|
|
753
|
+
// APP_PING message before sending the HELLO_REQ. Instead, the dapp was sending the HELLO_REQ
|
|
754
|
+
// immediately upon connection to the websocket server regardless of wether or not an
|
|
755
|
+
// APP_PING was sent by the wallet/websocket server. We must continue to support this behavior
|
|
756
|
+
// in case the user is using a wallet that has not updated their walletlib implementation.
|
|
757
|
+
const { associationKeypair } = state;
|
|
758
|
+
const ecdhKeypair = yield generateECDHKeypair();
|
|
759
|
+
socket.send(yield createHelloReq(ecdhKeypair.publicKey, associationKeypair.privateKey));
|
|
760
|
+
state = {
|
|
761
|
+
__type: 'hello_req_sent',
|
|
762
|
+
associationPublicKey: associationKeypair.publicKey,
|
|
763
|
+
ecdhPrivateKey: ecdhKeypair.privateKey,
|
|
764
|
+
};
|
|
765
|
+
});
|
|
766
|
+
const handleClose = (evt) => {
|
|
767
|
+
if (evt.wasClean) {
|
|
768
|
+
state = { __type: 'disconnected' };
|
|
769
|
+
}
|
|
770
|
+
else {
|
|
771
|
+
reject(new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_SESSION_CLOSED, `The wallet session dropped unexpectedly (${evt.code}: ${evt.reason}).`, { closeEvent: evt }));
|
|
772
|
+
}
|
|
773
|
+
disposeSocket();
|
|
774
|
+
};
|
|
775
|
+
const handleError = (_evt) => __awaiter(this, void 0, void 0, function* () {
|
|
776
|
+
disposeSocket();
|
|
777
|
+
if (Date.now() - connectionStartTime >= WEBSOCKET_CONNECTION_CONFIG.timeoutMs) {
|
|
778
|
+
reject(new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_SESSION_TIMEOUT, `Failed to connect to the wallet websocket at ${websocketURL}.`));
|
|
779
|
+
}
|
|
780
|
+
else {
|
|
781
|
+
yield new Promise((resolve) => {
|
|
782
|
+
const retryDelayMs = getNextRetryDelayMs();
|
|
783
|
+
retryWaitTimeoutId = window.setTimeout(resolve, retryDelayMs);
|
|
784
|
+
});
|
|
785
|
+
attemptSocketConnection();
|
|
786
|
+
}
|
|
787
|
+
});
|
|
788
|
+
const handleMessage = (evt) => __awaiter(this, void 0, void 0, function* () {
|
|
789
|
+
const responseBuffer = yield evt.data.arrayBuffer();
|
|
790
|
+
switch (state.__type) {
|
|
791
|
+
case 'connecting':
|
|
792
|
+
if (responseBuffer.byteLength !== 0) {
|
|
793
|
+
throw new Error('Encountered unexpected message while connecting');
|
|
794
|
+
}
|
|
795
|
+
const ecdhKeypair = yield generateECDHKeypair();
|
|
796
|
+
socket.send(yield createHelloReq(ecdhKeypair.publicKey, associationKeypair.privateKey));
|
|
797
|
+
state = {
|
|
798
|
+
__type: 'hello_req_sent',
|
|
799
|
+
associationPublicKey: associationKeypair.publicKey,
|
|
800
|
+
ecdhPrivateKey: ecdhKeypair.privateKey,
|
|
801
|
+
};
|
|
802
|
+
break;
|
|
803
|
+
case 'connected':
|
|
804
|
+
try {
|
|
805
|
+
const sequenceNumberVector = responseBuffer.slice(0, SEQUENCE_NUMBER_BYTES);
|
|
806
|
+
const sequenceNumber = getSequenceNumberFromByteArray(sequenceNumberVector);
|
|
807
|
+
if (sequenceNumber !== (lastKnownInboundSequenceNumber + 1)) {
|
|
808
|
+
throw new Error('Encrypted message has invalid sequence number');
|
|
809
|
+
}
|
|
810
|
+
lastKnownInboundSequenceNumber = sequenceNumber;
|
|
811
|
+
const jsonRpcMessage = yield decryptJsonRpcMessage(responseBuffer, state.sharedSecret);
|
|
812
|
+
const responsePromise = jsonRpcResponsePromises[jsonRpcMessage.id];
|
|
813
|
+
delete jsonRpcResponsePromises[jsonRpcMessage.id];
|
|
814
|
+
responsePromise.resolve(jsonRpcMessage.result);
|
|
815
|
+
}
|
|
816
|
+
catch (e) {
|
|
817
|
+
if (e instanceof SolanaMobileWalletAdapterProtocolError) {
|
|
818
|
+
const responsePromise = jsonRpcResponsePromises[e.jsonRpcMessageId];
|
|
819
|
+
delete jsonRpcResponsePromises[e.jsonRpcMessageId];
|
|
820
|
+
responsePromise.reject(e);
|
|
821
|
+
}
|
|
822
|
+
else {
|
|
823
|
+
throw e;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
break;
|
|
827
|
+
case 'hello_req_sent': {
|
|
828
|
+
// if we receive an APP_PING message (empty message), resend the HELLO_REQ (see above)
|
|
829
|
+
if (responseBuffer.byteLength === 0) {
|
|
830
|
+
const ecdhKeypair = yield generateECDHKeypair();
|
|
831
|
+
socket.send(yield createHelloReq(ecdhKeypair.publicKey, associationKeypair.privateKey));
|
|
832
|
+
state = {
|
|
833
|
+
__type: 'hello_req_sent',
|
|
834
|
+
associationPublicKey: associationKeypair.publicKey,
|
|
835
|
+
ecdhPrivateKey: ecdhKeypair.privateKey,
|
|
836
|
+
};
|
|
837
|
+
break;
|
|
838
|
+
}
|
|
839
|
+
const sharedSecret = yield parseHelloRsp(responseBuffer, state.associationPublicKey, state.ecdhPrivateKey);
|
|
840
|
+
const sessionPropertiesBuffer = responseBuffer.slice(ENCODED_PUBLIC_KEY_LENGTH_BYTES);
|
|
841
|
+
const sessionProperties = sessionPropertiesBuffer.byteLength !== 0
|
|
842
|
+
? yield (() => __awaiter(this, void 0, void 0, function* () {
|
|
843
|
+
const sequenceNumberVector = sessionPropertiesBuffer.slice(0, SEQUENCE_NUMBER_BYTES);
|
|
844
|
+
const sequenceNumber = getSequenceNumberFromByteArray(sequenceNumberVector);
|
|
845
|
+
if (sequenceNumber !== (lastKnownInboundSequenceNumber + 1)) {
|
|
846
|
+
throw new Error('Encrypted message has invalid sequence number');
|
|
847
|
+
}
|
|
848
|
+
lastKnownInboundSequenceNumber = sequenceNumber;
|
|
849
|
+
return parseSessionProps(sessionPropertiesBuffer, sharedSecret);
|
|
850
|
+
}))() : { protocol_version: 'legacy' };
|
|
851
|
+
state = { __type: 'connected', sharedSecret, sessionProperties };
|
|
852
|
+
const wallet = createMobileWalletProxy(sessionProperties.protocol_version, (method, params) => __awaiter(this, void 0, void 0, function* () {
|
|
853
|
+
const id = nextJsonRpcMessageId++;
|
|
854
|
+
socket.send(yield encryptJsonRpcMessage({
|
|
855
|
+
id,
|
|
856
|
+
jsonrpc: '2.0',
|
|
857
|
+
method,
|
|
858
|
+
params: params !== null && params !== void 0 ? params : {},
|
|
859
|
+
}, sharedSecret));
|
|
860
|
+
return new Promise((resolve, reject) => {
|
|
861
|
+
jsonRpcResponsePromises[id] = {
|
|
862
|
+
resolve(result) {
|
|
863
|
+
switch (method) {
|
|
864
|
+
case 'authorize':
|
|
865
|
+
case 'reauthorize': {
|
|
866
|
+
const { wallet_uri_base } = result;
|
|
867
|
+
if (wallet_uri_base != null) {
|
|
868
|
+
try {
|
|
869
|
+
assertSecureEndpointSpecificURI(wallet_uri_base);
|
|
870
|
+
}
|
|
871
|
+
catch (e) {
|
|
872
|
+
reject(e);
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
break;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
resolve(result);
|
|
880
|
+
},
|
|
881
|
+
reject,
|
|
882
|
+
};
|
|
883
|
+
});
|
|
884
|
+
}));
|
|
885
|
+
try {
|
|
886
|
+
resolve(yield callback(wallet));
|
|
887
|
+
}
|
|
888
|
+
catch (e) {
|
|
889
|
+
reject(e);
|
|
890
|
+
}
|
|
891
|
+
finally {
|
|
892
|
+
disposeSocket();
|
|
893
|
+
socket.close();
|
|
894
|
+
}
|
|
895
|
+
break;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
let disposeSocket;
|
|
900
|
+
let retryWaitTimeoutId;
|
|
901
|
+
const attemptSocketConnection = () => {
|
|
902
|
+
if (disposeSocket) {
|
|
903
|
+
disposeSocket();
|
|
904
|
+
}
|
|
905
|
+
state = { __type: 'connecting', associationKeypair };
|
|
906
|
+
if (connectionStartTime === undefined) {
|
|
907
|
+
connectionStartTime = Date.now();
|
|
908
|
+
}
|
|
909
|
+
socket = new WebSocket(websocketURL, [WEBSOCKET_PROTOCOL_BINARY]);
|
|
910
|
+
socket.addEventListener('open', handleOpen);
|
|
911
|
+
socket.addEventListener('close', handleClose);
|
|
912
|
+
socket.addEventListener('error', handleError);
|
|
913
|
+
socket.addEventListener('message', handleMessage);
|
|
914
|
+
disposeSocket = () => {
|
|
915
|
+
window.clearTimeout(retryWaitTimeoutId);
|
|
916
|
+
socket.removeEventListener('open', handleOpen);
|
|
917
|
+
socket.removeEventListener('close', handleClose);
|
|
918
|
+
socket.removeEventListener('error', handleError);
|
|
919
|
+
socket.removeEventListener('message', handleMessage);
|
|
920
|
+
};
|
|
921
|
+
};
|
|
922
|
+
attemptSocketConnection();
|
|
923
|
+
});
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
function startRemoteScenario(config) {
|
|
927
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
928
|
+
assertSecureContext();
|
|
929
|
+
const associationKeypair = yield generateAssociationKeypair();
|
|
930
|
+
const websocketURL = `wss://${config === null || config === void 0 ? void 0 : config.remoteHostAuthority}/reflect`;
|
|
931
|
+
let connectionStartTime;
|
|
932
|
+
const getNextRetryDelayMs = (() => {
|
|
933
|
+
const schedule = [...WEBSOCKET_CONNECTION_CONFIG.retryDelayScheduleMs];
|
|
934
|
+
return () => (schedule.length > 1 ? schedule.shift() : schedule[0]);
|
|
935
|
+
})();
|
|
936
|
+
let nextJsonRpcMessageId = 1;
|
|
937
|
+
let lastKnownInboundSequenceNumber = 0;
|
|
938
|
+
let encoding;
|
|
939
|
+
let state = { __type: 'disconnected' };
|
|
940
|
+
let socket;
|
|
941
|
+
let disposeSocket;
|
|
942
|
+
let decodeBytes = (evt) => __awaiter(this, void 0, void 0, function* () {
|
|
943
|
+
if (encoding == 'base64') { // base64 encoding
|
|
944
|
+
const message = yield evt.data;
|
|
945
|
+
return toUint8Array(message).buffer;
|
|
946
|
+
}
|
|
947
|
+
else {
|
|
948
|
+
return yield evt.data.arrayBuffer();
|
|
949
|
+
}
|
|
950
|
+
});
|
|
951
|
+
// Reflector Connection Phase
|
|
952
|
+
// here we connect to the reflector and wait for the REFLECTOR_ID message
|
|
953
|
+
// so we build the association URL and return that back to the caller
|
|
954
|
+
const associationUrl = yield new Promise((resolve, reject) => {
|
|
955
|
+
const handleOpen = () => __awaiter(this, void 0, void 0, function* () {
|
|
956
|
+
if (state.__type !== 'connecting') {
|
|
957
|
+
console.warn('Expected adapter state to be `connecting` at the moment the websocket opens. ' +
|
|
958
|
+
`Got \`${state.__type}\`.`);
|
|
959
|
+
return;
|
|
960
|
+
}
|
|
961
|
+
if (socket.protocol.includes(WEBSOCKET_PROTOCOL_BASE64)) {
|
|
962
|
+
encoding = 'base64';
|
|
963
|
+
}
|
|
964
|
+
else {
|
|
965
|
+
encoding = 'binary';
|
|
966
|
+
}
|
|
967
|
+
socket.removeEventListener('open', handleOpen);
|
|
968
|
+
});
|
|
969
|
+
const handleClose = (evt) => {
|
|
970
|
+
if (evt.wasClean) {
|
|
971
|
+
state = { __type: 'disconnected' };
|
|
972
|
+
}
|
|
973
|
+
else {
|
|
974
|
+
reject(new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_SESSION_CLOSED, `The wallet session dropped unexpectedly (${evt.code}: ${evt.reason}).`, { closeEvent: evt }));
|
|
975
|
+
}
|
|
976
|
+
disposeSocket();
|
|
977
|
+
};
|
|
978
|
+
const handleError = (_evt) => __awaiter(this, void 0, void 0, function* () {
|
|
979
|
+
disposeSocket();
|
|
980
|
+
if (Date.now() - connectionStartTime >= WEBSOCKET_CONNECTION_CONFIG.timeoutMs) {
|
|
981
|
+
reject(new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_SESSION_TIMEOUT, `Failed to connect to the wallet websocket at ${websocketURL}.`));
|
|
982
|
+
}
|
|
983
|
+
else {
|
|
984
|
+
yield new Promise((resolve) => {
|
|
985
|
+
const retryDelayMs = getNextRetryDelayMs();
|
|
986
|
+
retryWaitTimeoutId = window.setTimeout(resolve, retryDelayMs);
|
|
987
|
+
});
|
|
988
|
+
attemptSocketConnection();
|
|
989
|
+
}
|
|
990
|
+
});
|
|
991
|
+
const handleReflectorIdMessage = (evt) => __awaiter(this, void 0, void 0, function* () {
|
|
992
|
+
const responseBuffer = yield decodeBytes(evt);
|
|
993
|
+
if (state.__type === 'connecting') {
|
|
994
|
+
if (responseBuffer.byteLength == 0) {
|
|
995
|
+
throw new Error('Encountered unexpected message while connecting');
|
|
996
|
+
}
|
|
997
|
+
const reflectorId = getReflectorIdFromByteArray(responseBuffer);
|
|
998
|
+
state = {
|
|
999
|
+
__type: 'reflector_id_received',
|
|
1000
|
+
reflectorId: reflectorId
|
|
1001
|
+
};
|
|
1002
|
+
const associationUrl = yield getRemoteAssociateAndroidIntentURL(associationKeypair.publicKey, config.remoteHostAuthority, reflectorId, config === null || config === void 0 ? void 0 : config.baseUri);
|
|
1003
|
+
socket.removeEventListener('message', handleReflectorIdMessage);
|
|
1004
|
+
resolve(associationUrl);
|
|
1005
|
+
}
|
|
1006
|
+
});
|
|
1007
|
+
let retryWaitTimeoutId;
|
|
1008
|
+
const attemptSocketConnection = () => {
|
|
1009
|
+
if (disposeSocket) {
|
|
1010
|
+
disposeSocket();
|
|
1011
|
+
}
|
|
1012
|
+
state = { __type: 'connecting', associationKeypair };
|
|
1013
|
+
if (connectionStartTime === undefined) {
|
|
1014
|
+
connectionStartTime = Date.now();
|
|
1015
|
+
}
|
|
1016
|
+
socket = new WebSocket(websocketURL, [WEBSOCKET_PROTOCOL_BINARY, WEBSOCKET_PROTOCOL_BASE64]);
|
|
1017
|
+
socket.addEventListener('open', handleOpen);
|
|
1018
|
+
socket.addEventListener('close', handleClose);
|
|
1019
|
+
socket.addEventListener('error', handleError);
|
|
1020
|
+
socket.addEventListener('message', handleReflectorIdMessage);
|
|
1021
|
+
disposeSocket = () => {
|
|
1022
|
+
window.clearTimeout(retryWaitTimeoutId);
|
|
1023
|
+
socket.removeEventListener('open', handleOpen);
|
|
1024
|
+
socket.removeEventListener('close', handleClose);
|
|
1025
|
+
socket.removeEventListener('error', handleError);
|
|
1026
|
+
socket.removeEventListener('message', handleReflectorIdMessage);
|
|
1027
|
+
};
|
|
1028
|
+
};
|
|
1029
|
+
attemptSocketConnection();
|
|
1030
|
+
});
|
|
1031
|
+
// Wallet Connection Phase
|
|
1032
|
+
// here we return the association URL (containing the reflector ID) to the caller +
|
|
1033
|
+
// a promise that will resolve the MobileWallet object once the wallet connects.
|
|
1034
|
+
let sessionEstablished = false;
|
|
1035
|
+
let handleClose;
|
|
1036
|
+
return { associationUrl, close: () => {
|
|
1037
|
+
socket.close();
|
|
1038
|
+
handleClose();
|
|
1039
|
+
}, wallet: new Promise((resolve, reject) => {
|
|
1040
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1041
|
+
const jsonRpcResponsePromises = {};
|
|
1042
|
+
const handleMessage = (evt) => __awaiter(this, void 0, void 0, function* () {
|
|
1043
|
+
const responseBuffer = yield decodeBytes(evt);
|
|
1044
|
+
switch (state.__type) {
|
|
1045
|
+
case 'reflector_id_received':
|
|
1046
|
+
if (responseBuffer.byteLength !== 0) {
|
|
1047
|
+
throw new Error('Encountered unexpected message while awaiting reflection');
|
|
1048
|
+
}
|
|
1049
|
+
const ecdhKeypair = yield generateECDHKeypair();
|
|
1050
|
+
const binaryMsg = yield createHelloReq(ecdhKeypair.publicKey, associationKeypair.privateKey);
|
|
1051
|
+
if (encoding == 'base64') {
|
|
1052
|
+
socket.send(fromUint8Array$1(binaryMsg));
|
|
1053
|
+
}
|
|
1054
|
+
else {
|
|
1055
|
+
socket.send(binaryMsg);
|
|
1056
|
+
}
|
|
1057
|
+
state = {
|
|
1058
|
+
__type: 'hello_req_sent',
|
|
1059
|
+
associationPublicKey: associationKeypair.publicKey,
|
|
1060
|
+
ecdhPrivateKey: ecdhKeypair.privateKey,
|
|
1061
|
+
};
|
|
1062
|
+
break;
|
|
1063
|
+
case 'connected':
|
|
1064
|
+
try {
|
|
1065
|
+
const sequenceNumberVector = responseBuffer.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
|
+
const jsonRpcMessage = yield decryptJsonRpcMessage(responseBuffer, state.sharedSecret);
|
|
1072
|
+
const responsePromise = jsonRpcResponsePromises[jsonRpcMessage.id];
|
|
1073
|
+
delete jsonRpcResponsePromises[jsonRpcMessage.id];
|
|
1074
|
+
responsePromise.resolve(jsonRpcMessage.result);
|
|
1075
|
+
}
|
|
1076
|
+
catch (e) {
|
|
1077
|
+
if (e instanceof SolanaMobileWalletAdapterProtocolError) {
|
|
1078
|
+
const responsePromise = jsonRpcResponsePromises[e.jsonRpcMessageId];
|
|
1079
|
+
delete jsonRpcResponsePromises[e.jsonRpcMessageId];
|
|
1080
|
+
responsePromise.reject(e);
|
|
1081
|
+
}
|
|
1082
|
+
else {
|
|
1083
|
+
throw e;
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
break;
|
|
1087
|
+
case 'hello_req_sent': {
|
|
1088
|
+
const sharedSecret = yield parseHelloRsp(responseBuffer, state.associationPublicKey, state.ecdhPrivateKey);
|
|
1089
|
+
const sessionPropertiesBuffer = responseBuffer.slice(ENCODED_PUBLIC_KEY_LENGTH_BYTES);
|
|
1090
|
+
const sessionProperties = sessionPropertiesBuffer.byteLength !== 0
|
|
1091
|
+
? yield (() => __awaiter(this, void 0, void 0, function* () {
|
|
1092
|
+
const sequenceNumberVector = sessionPropertiesBuffer.slice(0, SEQUENCE_NUMBER_BYTES);
|
|
1093
|
+
const sequenceNumber = getSequenceNumberFromByteArray(sequenceNumberVector);
|
|
1094
|
+
if (sequenceNumber !== (lastKnownInboundSequenceNumber + 1)) {
|
|
1095
|
+
throw new Error('Encrypted message has invalid sequence number');
|
|
1096
|
+
}
|
|
1097
|
+
lastKnownInboundSequenceNumber = sequenceNumber;
|
|
1098
|
+
return parseSessionProps(sessionPropertiesBuffer, sharedSecret);
|
|
1099
|
+
}))() : { protocol_version: 'legacy' };
|
|
1100
|
+
state = { __type: 'connected', sharedSecret, sessionProperties };
|
|
1101
|
+
const wallet = createMobileWalletProxy(sessionProperties.protocol_version, (method, params) => __awaiter(this, void 0, void 0, function* () {
|
|
1102
|
+
const id = nextJsonRpcMessageId++;
|
|
1103
|
+
const binaryMsg = yield encryptJsonRpcMessage({
|
|
1104
|
+
id,
|
|
1105
|
+
jsonrpc: '2.0',
|
|
1106
|
+
method,
|
|
1107
|
+
params: params !== null && params !== void 0 ? params : {},
|
|
1108
|
+
}, sharedSecret);
|
|
1109
|
+
if (encoding == 'base64') {
|
|
1110
|
+
socket.send(fromUint8Array$1(binaryMsg));
|
|
1111
|
+
}
|
|
1112
|
+
else {
|
|
1113
|
+
socket.send(binaryMsg);
|
|
1114
|
+
}
|
|
1115
|
+
return new Promise((resolve, reject) => {
|
|
1116
|
+
jsonRpcResponsePromises[id] = {
|
|
1117
|
+
resolve(result) {
|
|
1118
|
+
switch (method) {
|
|
1119
|
+
case 'authorize':
|
|
1120
|
+
case 'reauthorize': {
|
|
1121
|
+
const { wallet_uri_base } = result;
|
|
1122
|
+
if (wallet_uri_base != null) {
|
|
1123
|
+
try {
|
|
1124
|
+
assertSecureEndpointSpecificURI(wallet_uri_base);
|
|
1125
|
+
}
|
|
1126
|
+
catch (e) {
|
|
1127
|
+
reject(e);
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
break;
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
resolve(result);
|
|
1135
|
+
},
|
|
1136
|
+
reject,
|
|
1137
|
+
};
|
|
1138
|
+
});
|
|
1139
|
+
}));
|
|
1140
|
+
sessionEstablished = true;
|
|
1141
|
+
try {
|
|
1142
|
+
resolve(wallet);
|
|
1143
|
+
}
|
|
1144
|
+
catch (e) {
|
|
1145
|
+
reject(e);
|
|
1146
|
+
}
|
|
1147
|
+
break;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
});
|
|
1151
|
+
socket.addEventListener('message', handleMessage);
|
|
1152
|
+
handleClose = () => {
|
|
1153
|
+
socket.removeEventListener('message', handleMessage);
|
|
1154
|
+
disposeSocket();
|
|
1155
|
+
if (!sessionEstablished) {
|
|
1156
|
+
reject(new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_SESSION_CLOSED, `The wallet session was closed before connection.`, { closeEvent: new CloseEvent('socket was closed before connection') }));
|
|
1157
|
+
}
|
|
1158
|
+
};
|
|
1159
|
+
}) };
|
|
1160
|
+
});
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
export { startRemoteScenario as s, transact as t };
|
|
1164
|
+
//# sourceMappingURL=index.browser-DZjyUgtx.esm.js.map
|