@solana-mobile/wallet-standard-mobile 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,19 @@
1
- import { SolanaSignIn, SolanaSignMessage, SolanaSignTransaction, SolanaSignAndSendTransaction } from '@solana/wallet-standard-features';
2
- import QRCode from 'qrcode';
3
- import { SolanaMobileWalletAdapterError, SolanaMobileWalletAdapterErrorCode, startScenario, startRemoteScenario } from '@solana-mobile/mobile-wallet-adapter-protocol';
4
- import { StandardEvents, StandardDisconnect, StandardConnect } from '@wallet-standard/features';
5
- import base58 from 'bs58';
6
- import { registerWallet } from '@wallet-standard/wallet';
7
- import { SOLANA_MAINNET_CHAIN } from '@solana/wallet-standard-chains';
8
-
1
+ import { SolanaSignAndSendTransaction, SolanaSignIn, SolanaSignMessage, SolanaSignTransaction } from "@solana/wallet-standard-features";
2
+ import { SolanaMobileWalletAdapterError, SolanaMobileWalletAdapterErrorCode, startRemoteScenario, startScenario } from "@solana-mobile/mobile-wallet-adapter-protocol";
3
+ import { StandardConnect, StandardDisconnect, StandardEvents } from "@wallet-standard/features";
4
+ import base58 from "bs58";
5
+ import QRCode from "qrcode";
6
+ import { registerWallet } from "@wallet-standard/wallet";
7
+ import { SOLANA_MAINNET_CHAIN } from "@solana/wallet-standard-chains";
8
+ //#region src/base64Utils.ts
9
+ function fromUint8Array(byteArray) {
10
+ return window.btoa(String.fromCharCode.call(null, ...byteArray));
11
+ }
12
+ function toUint8Array(base64EncodedByteArray) {
13
+ return new Uint8Array(window.atob(base64EncodedByteArray).split("").map((c) => c.charCodeAt(0)));
14
+ }
15
+ //#endregion
16
+ //#region src/embedded-modal/loadingSpinner.ts
9
17
  const modalHtml$1 = `
10
18
  <div class="mobile-wallet-adapter-embedded-loading-indicator" role="dialog" aria-modal="true" aria-labelledby="modal-title">
11
19
  <div data-modal-close style="position: absolute; width: 100%; height: 100%;"></div>
@@ -86,92 +94,76 @@ const css$6 = `
86
94
  }
87
95
  }
88
96
  `;
89
- class EmbeddedLoadingSpinner {
90
- #root = null;
91
- #eventListeners = {};
92
- #listenersAttached = false;
93
- dom = null;
94
- constructor() {
95
- // Bind methods to ensure `this` context is correct
96
- this.init = this.init.bind(this);
97
- this.#root = document.getElementById('mobile-wallet-adapter-embedded-root-ui');
98
- }
99
- async init() {
100
- console.log('Injecting modal');
101
- this.#injectHTML();
102
- }
103
- open = () => {
104
- console.debug('Modal open');
105
- this.#attachEventListeners();
106
- if (this.#root) {
107
- this.#root.style.display = 'flex';
108
- }
109
- };
110
- close = (event = undefined) => {
111
- console.debug('Modal close');
112
- this.#removeEventListeners();
113
- if (this.#root) {
114
- this.#root.style.display = 'none';
115
- }
116
- this.#eventListeners['close']?.forEach((listener) => listener(event));
117
- };
118
- addEventListener(event, listener) {
119
- this.#eventListeners[event]?.push(listener) || (this.#eventListeners[event] = [listener]);
120
- return () => this.removeEventListener(event, listener);
121
- }
122
- removeEventListener(event, listener) {
123
- this.#eventListeners[event] = this.#eventListeners[event]?.filter((existingListener) => listener !== existingListener);
124
- }
125
- #injectHTML() {
126
- // Check if already injected by checking if shadow DOM exists
127
- if (this.dom) {
128
- return;
129
- }
130
- // Create a container for the modal
131
- this.#root = document.createElement('div');
132
- this.#root.id = 'mobile-wallet-adapter-embedded-root-ui';
133
- this.#root.innerHTML = modalHtml$1;
134
- this.#root.style.display = 'none';
135
- // Apply styles
136
- const styles = document.createElement('style');
137
- styles.id = 'mobile-wallet-adapter-embedded-modal-styles';
138
- styles.textContent = css$6;
139
- // Create a shadow DOM to encapsulate the modal
140
- const host = document.createElement('div');
141
- this.dom = host.attachShadow({ mode: 'closed' });
142
- // Pass the CSS variable to the Shadow DOM
143
- host.style.setProperty('--spinner-color', '#FFFFFF');
144
- this.dom.appendChild(styles);
145
- this.dom.appendChild(this.#root);
146
- // Append the shadow DOM host to the body
147
- document.body.appendChild(host);
148
- }
149
- #attachEventListeners() {
150
- if (!this.#root || this.#listenersAttached)
151
- return;
152
- const closers = [...this.#root.querySelectorAll('[data-modal-close]')];
153
- closers.forEach(closer => closer?.addEventListener('click', (event) => { this.close(event); }));
154
- window.addEventListener('load', this.close);
155
- document.addEventListener('keydown', this.#handleKeyDown);
156
- this.#listenersAttached = true;
157
- }
158
- #removeEventListeners() {
159
- if (!this.#listenersAttached)
160
- return;
161
- window.removeEventListener('load', this.close);
162
- document.removeEventListener('keydown', this.#handleKeyDown);
163
- if (!this.#root)
164
- return;
165
- const closers = [...this.#root.querySelectorAll('[data-modal-close]')];
166
- closers.forEach(closer => closer?.removeEventListener('click', this.close));
167
- this.#listenersAttached = false;
168
- }
169
- #handleKeyDown = (event) => {
170
- if (event.key === 'Escape')
171
- this.close(event);
172
- };
173
- }
174
-
97
+ var EmbeddedLoadingSpinner = class {
98
+ #root = null;
99
+ #eventListeners = {};
100
+ #listenersAttached = false;
101
+ dom = null;
102
+ constructor() {
103
+ this.init = this.init.bind(this);
104
+ this.#root = document.getElementById("mobile-wallet-adapter-embedded-root-ui");
105
+ }
106
+ async init() {
107
+ console.log("Injecting modal");
108
+ this.#injectHTML();
109
+ }
110
+ open = () => {
111
+ console.debug("Modal open");
112
+ this.#attachEventListeners();
113
+ if (this.#root) this.#root.style.display = "flex";
114
+ };
115
+ close = (event = void 0) => {
116
+ console.debug("Modal close");
117
+ this.#removeEventListeners();
118
+ if (this.#root) this.#root.style.display = "none";
119
+ this.#eventListeners["close"]?.forEach((listener) => listener(event));
120
+ };
121
+ addEventListener(event, listener) {
122
+ this.#eventListeners[event]?.push(listener) || (this.#eventListeners[event] = [listener]);
123
+ return () => this.removeEventListener(event, listener);
124
+ }
125
+ removeEventListener(event, listener) {
126
+ this.#eventListeners[event] = this.#eventListeners[event]?.filter((existingListener) => listener !== existingListener);
127
+ }
128
+ #injectHTML() {
129
+ if (this.dom) return;
130
+ this.#root = document.createElement("div");
131
+ this.#root.id = "mobile-wallet-adapter-embedded-root-ui";
132
+ this.#root.innerHTML = modalHtml$1;
133
+ this.#root.style.display = "none";
134
+ const styles = document.createElement("style");
135
+ styles.id = "mobile-wallet-adapter-embedded-modal-styles";
136
+ styles.textContent = css$6;
137
+ const host = document.createElement("div");
138
+ this.dom = host.attachShadow({ mode: "closed" });
139
+ host.style.setProperty("--spinner-color", "#FFFFFF");
140
+ this.dom.appendChild(styles);
141
+ this.dom.appendChild(this.#root);
142
+ document.body.appendChild(host);
143
+ }
144
+ #attachEventListeners() {
145
+ if (!this.#root || this.#listenersAttached) return;
146
+ [...this.#root.querySelectorAll("[data-modal-close]")].forEach((closer) => closer?.addEventListener("click", (event) => {
147
+ this.close(event);
148
+ }));
149
+ window.addEventListener("load", this.close);
150
+ document.addEventListener("keydown", this.#handleKeyDown);
151
+ this.#listenersAttached = true;
152
+ }
153
+ #removeEventListeners() {
154
+ if (!this.#listenersAttached) return;
155
+ window.removeEventListener("load", this.close);
156
+ document.removeEventListener("keydown", this.#handleKeyDown);
157
+ if (!this.#root) return;
158
+ [...this.#root.querySelectorAll("[data-modal-close]")].forEach((closer) => closer?.removeEventListener("click", this.close));
159
+ this.#listenersAttached = false;
160
+ }
161
+ #handleKeyDown = (event) => {
162
+ if (event.key === "Escape") this.close(event);
163
+ };
164
+ };
165
+ //#endregion
166
+ //#region src/embedded-modal/modal.ts
175
167
  const modalHtml = `
176
168
  <div class="mobile-wallet-adapter-embedded-modal-container" role="dialog" aria-modal="true" aria-labelledby="modal-title">
177
169
  <div data-modal-close style="position: absolute; width: 100%; height: 100%;"></div>
@@ -193,7 +185,7 @@ const css$5 = `
193
185
  justify-content: center; /* Center horizontally */
194
186
  align-items: center; /* Center vertically */
195
187
  position: fixed; /* Stay in place */
196
- z-index: 1; /* Sit on top */
188
+ z-index: 2147483647; /* Sit on top */
197
189
  left: 0;
198
190
  top: 0;
199
191
  width: 100%; /* Full width */
@@ -249,124 +241,100 @@ const fonts = `
249
241
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
250
242
  <link href="https://fonts.googleapis.com/css2?family=Inter+Tight:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
251
243
  `;
252
- class EmbeddedModal {
253
- #root = null;
254
- #eventListeners = {};
255
- #listenersAttached = false;
256
- dom = null;
257
- constructor() {
258
- // Bind methods to ensure `this` context is correct
259
- this.init = this.init.bind(this);
260
- this.#root = document.getElementById('mobile-wallet-adapter-embedded-root-ui');
261
- }
262
- async init() {
263
- console.log('Injecting modal');
264
- this.#injectHTML();
265
- }
266
- open = () => {
267
- console.debug('Modal open');
268
- this.#attachEventListeners();
269
- if (this.#root) {
270
- this.#root.style.display = 'flex';
271
- }
272
- };
273
- close = (event = undefined) => {
274
- console.debug('Modal close');
275
- this.#removeEventListeners();
276
- if (this.#root) {
277
- this.#root.style.display = 'none';
278
- }
279
- this.#eventListeners['close']?.forEach((listener) => listener(event));
280
- };
281
- addEventListener(event, listener) {
282
- this.#eventListeners[event]?.push(listener) || (this.#eventListeners[event] = [listener]);
283
- return () => this.removeEventListener(event, listener);
284
- }
285
- removeEventListener(event, listener) {
286
- this.#eventListeners[event] = this.#eventListeners[event]?.filter((existingListener) => listener !== existingListener);
287
- }
288
- #injectHTML() {
289
- // Check if the HTML has already been injected
290
- if (document.getElementById('mobile-wallet-adapter-embedded-root-ui')) {
291
- if (!this.#root)
292
- this.#root = document.getElementById('mobile-wallet-adapter-embedded-root-ui');
293
- return;
294
- }
295
- // Create a container for the modal
296
- this.#root = document.createElement('div');
297
- this.#root.id = 'mobile-wallet-adapter-embedded-root-ui';
298
- this.#root.innerHTML = modalHtml;
299
- this.#root.style.display = 'none';
300
- // Add modal content
301
- const content = this.#root.querySelector('.mobile-wallet-adapter-embedded-modal-content');
302
- if (content)
303
- content.innerHTML = this.contentHtml;
304
- // Apply styles
305
- const styles = document.createElement('style');
306
- styles.id = 'mobile-wallet-adapter-embedded-modal-styles';
307
- styles.textContent = css$5 + this.contentStyles;
308
- // Create a shadow DOM to encapsulate the modal
309
- const host = document.createElement('div');
310
- host.innerHTML = fonts;
311
- this.dom = host.attachShadow({ mode: 'closed' });
312
- this.dom.appendChild(styles);
313
- this.dom.appendChild(this.#root);
314
- // Append the shadow DOM host to the body
315
- document.body.appendChild(host);
316
- }
317
- #attachEventListeners() {
318
- if (!this.#root || this.#listenersAttached)
319
- return;
320
- const closers = [...this.#root.querySelectorAll('[data-modal-close]')];
321
- closers.forEach(closer => closer?.addEventListener('click', this.close));
322
- window.addEventListener('load', this.close);
323
- document.addEventListener('keydown', this.#handleKeyDown);
324
- this.#listenersAttached = true;
325
- }
326
- #removeEventListeners() {
327
- if (!this.#listenersAttached)
328
- return;
329
- window.removeEventListener('load', this.close);
330
- document.removeEventListener('keydown', this.#handleKeyDown);
331
- if (!this.#root)
332
- return;
333
- const closers = [...this.#root.querySelectorAll('[data-modal-close]')];
334
- closers.forEach(closer => closer?.removeEventListener('click', this.close));
335
- this.#listenersAttached = false;
336
- }
337
- #handleKeyDown = (event) => {
338
- if (event.key === 'Escape')
339
- this.close(event);
340
- };
341
- }
342
-
343
- class RemoteConnectionModal extends EmbeddedModal {
344
- contentStyles = css$4;
345
- contentHtml = QRCodeHtml;
346
- async initWithQR(qrCode) {
347
- super.init();
348
- this.populateQRCode(qrCode);
349
- }
350
- async populateQRCode(qrUrl) {
351
- const qrcodeContainer = this.dom?.getElementById('mobile-wallet-adapter-embedded-modal-qr-code-container');
352
- if (qrcodeContainer) {
353
- const qrCodeElement = await QRCode.toCanvas(qrUrl, { width: 200, margin: 0 });
354
- if (qrcodeContainer.firstElementChild !== null) {
355
- qrcodeContainer.replaceChild(qrCodeElement, qrcodeContainer.firstElementChild);
356
- }
357
- else
358
- qrcodeContainer.appendChild(qrCodeElement);
359
- // remove the loading placeholder for cleanup
360
- const qrPlaceholder = this.dom?.getElementById('mobile-wallet-adapter-embedded-modal-qr-placeholder');
361
- if (qrPlaceholder) {
362
- qrPlaceholder.style.display = 'none';
363
- }
364
- }
365
- else {
366
- console.error('QRCode Container not found');
367
- }
368
- }
369
- }
244
+ var EmbeddedModal = class {
245
+ #root = null;
246
+ #eventListeners = {};
247
+ #listenersAttached = false;
248
+ dom = null;
249
+ constructor() {
250
+ this.init = this.init.bind(this);
251
+ this.#root = document.getElementById("mobile-wallet-adapter-embedded-root-ui");
252
+ }
253
+ async init() {
254
+ console.log("Injecting modal");
255
+ this.#injectHTML();
256
+ }
257
+ open = () => {
258
+ console.debug("Modal open");
259
+ this.#attachEventListeners();
260
+ if (this.#root) this.#root.style.display = "flex";
261
+ };
262
+ close = (event = void 0) => {
263
+ console.debug("Modal close");
264
+ this.#removeEventListeners();
265
+ if (this.#root) this.#root.style.display = "none";
266
+ this.#eventListeners["close"]?.forEach((listener) => listener(event));
267
+ };
268
+ addEventListener(event, listener) {
269
+ this.#eventListeners[event]?.push(listener) || (this.#eventListeners[event] = [listener]);
270
+ return () => this.removeEventListener(event, listener);
271
+ }
272
+ removeEventListener(event, listener) {
273
+ this.#eventListeners[event] = this.#eventListeners[event]?.filter((existingListener) => listener !== existingListener);
274
+ }
275
+ #injectHTML() {
276
+ if (document.getElementById("mobile-wallet-adapter-embedded-root-ui")) {
277
+ if (!this.#root) this.#root = document.getElementById("mobile-wallet-adapter-embedded-root-ui");
278
+ return;
279
+ }
280
+ this.#root = document.createElement("div");
281
+ this.#root.id = "mobile-wallet-adapter-embedded-root-ui";
282
+ this.#root.innerHTML = modalHtml;
283
+ this.#root.style.display = "none";
284
+ const content = this.#root.querySelector(".mobile-wallet-adapter-embedded-modal-content");
285
+ if (content) content.innerHTML = this.contentHtml;
286
+ const styles = document.createElement("style");
287
+ styles.id = "mobile-wallet-adapter-embedded-modal-styles";
288
+ styles.textContent = css$5 + this.contentStyles;
289
+ const host = document.createElement("div");
290
+ host.innerHTML = fonts;
291
+ this.dom = host.attachShadow({ mode: "closed" });
292
+ this.dom.appendChild(styles);
293
+ this.dom.appendChild(this.#root);
294
+ document.body.appendChild(host);
295
+ }
296
+ #attachEventListeners() {
297
+ if (!this.#root || this.#listenersAttached) return;
298
+ [...this.#root.querySelectorAll("[data-modal-close]")].forEach((closer) => closer?.addEventListener("click", this.close));
299
+ window.addEventListener("load", this.close);
300
+ document.addEventListener("keydown", this.#handleKeyDown);
301
+ this.#listenersAttached = true;
302
+ }
303
+ #removeEventListeners() {
304
+ if (!this.#listenersAttached) return;
305
+ window.removeEventListener("load", this.close);
306
+ document.removeEventListener("keydown", this.#handleKeyDown);
307
+ if (!this.#root) return;
308
+ [...this.#root.querySelectorAll("[data-modal-close]")].forEach((closer) => closer?.removeEventListener("click", this.close));
309
+ this.#listenersAttached = false;
310
+ }
311
+ #handleKeyDown = (event) => {
312
+ if (event.key === "Escape") this.close(event);
313
+ };
314
+ };
315
+ //#endregion
316
+ //#region src/embedded-modal/remoteConnectionModal.ts
317
+ var RemoteConnectionModal = class extends EmbeddedModal {
318
+ contentStyles = css$4;
319
+ contentHtml = QRCodeHtml;
320
+ async initWithQR(qrCode) {
321
+ super.init();
322
+ this.populateQRCode(qrCode);
323
+ }
324
+ async populateQRCode(qrUrl) {
325
+ const qrcodeContainer = this.dom?.getElementById("mobile-wallet-adapter-embedded-modal-qr-code-container");
326
+ if (qrcodeContainer) {
327
+ const qrCodeElement = await QRCode.toCanvas(qrUrl, {
328
+ width: 200,
329
+ margin: 0
330
+ });
331
+ if (qrcodeContainer.firstElementChild !== null) qrcodeContainer.replaceChild(qrCodeElement, qrcodeContainer.firstElementChild);
332
+ else qrcodeContainer.appendChild(qrCodeElement);
333
+ const qrPlaceholder = this.dom?.getElementById("mobile-wallet-adapter-embedded-modal-qr-placeholder");
334
+ if (qrPlaceholder) qrPlaceholder.style.display = "none";
335
+ } else console.error("QRCode Container not found");
336
+ }
337
+ };
370
338
  const QRCodeHtml = `
371
339
  <div class="mobile-wallet-adapter-embedded-modal-qr-content">
372
340
  <div>
@@ -626,36 +594,25 @@ const css$4 = `
626
594
  animation: spinRight 2.5s cubic-bezier(.2,0,.8,1) infinite;
627
595
  }
628
596
  `;
629
-
630
- const icon = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03IDIuNUgxN0MxNy44Mjg0IDIuNSAxOC41IDMuMTcxNTcgMTguNSA0VjIwQzE4LjUgMjAuODI4NCAxNy44Mjg0IDIxLjUgMTcgMjEuNUg3QzYuMTcxNTcgMjEuNSA1LjUgMjAuODI4NCA1LjUgMjBWNEM1LjUgMy4xNzE1NyA2LjE3MTU3IDIuNSA3IDIuNVpNMyA0QzMgMS43OTA4NiA0Ljc5MDg2IDAgNyAwSDE3QzE5LjIwOTEgMCAyMSAxLjc5MDg2IDIxIDRWMjBDMjEgMjIuMjA5MSAxOS4yMDkxIDI0IDE3IDI0SDdDNC43OTA4NiAyNCAzIDIyLjIwOTEgMyAyMFY0Wk0xMSA0LjYxNTM4QzEwLjQ0NzcgNC42MTUzOCAxMCA1LjA2MzEgMTAgNS42MTUzOFY2LjM4NDYyQzEwIDYuOTM2OSAxMC40NDc3IDcuMzg0NjIgMTEgNy4zODQ2MkgxM0MxMy41NTIzIDcuMzg0NjIgMTQgNi45MzY5IDE0IDYuMzg0NjJWNS42MTUzOEMxNCA1LjA2MzEgMTMuNTUyMyA0LjYxNTM4IDEzIDQuNjE1MzhIMTFaIiBmaWxsPSIjRENCOEZGIi8+Cjwvc3ZnPgo=';
631
-
632
- function fromUint8Array(byteArray) {
633
- return window.btoa(String.fromCharCode.call(null, ...byteArray));
634
- }
635
- function toUint8Array(base64EncodedByteArray) {
636
- return new Uint8Array(window
637
- .atob(base64EncodedByteArray)
638
- .split('')
639
- .map((c) => c.charCodeAt(0)));
640
- }
641
-
642
- class LocalConnectionModal extends EmbeddedModal {
643
- contentStyles = css$3;
644
- contentHtml = ErrorDialogHtml$3;
645
- initWithCallback(callback) {
646
- super.init();
647
- this.#prepareLaunchAction(callback);
648
- }
649
- #prepareLaunchAction(callback) {
650
- const launchButton = this.dom?.getElementById("mobile-wallet-adapter-launch-action");
651
- const listener = async () => {
652
- launchButton?.removeEventListener('click', listener);
653
- this.close();
654
- callback();
655
- };
656
- launchButton?.addEventListener('click', listener);
657
- }
658
- }
597
+ //#endregion
598
+ //#region src/embedded-modal/localConnectionModal.ts
599
+ var LocalConnectionModal = class extends EmbeddedModal {
600
+ contentStyles = css$3;
601
+ contentHtml = ErrorDialogHtml$3;
602
+ initWithCallback(callback) {
603
+ super.init();
604
+ this.#prepareLaunchAction(callback);
605
+ }
606
+ #prepareLaunchAction(callback) {
607
+ const launchButton = this.dom?.getElementById("mobile-wallet-adapter-launch-action");
608
+ const listener = async () => {
609
+ launchButton?.removeEventListener("click", listener);
610
+ this.close();
611
+ callback();
612
+ };
613
+ launchButton?.addEventListener("click", listener);
614
+ }
615
+ };
659
616
  const ErrorDialogHtml$3 = `
660
617
  <svg class="mobile-wallet-adapter-embedded-modal-launch-icon" width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
661
618
  <path d="M21.6 48C7.2 48 0 40.8 0 26.4V21.6C0 7.2 7.2 0 21.6 0H26.4C40.8 0 48 7.2 48 21.6V26.4C48 40.8 40.8 48 26.4 48H21.6Z" fill="#15994E"/>
@@ -713,28 +670,27 @@ const css$3 = `
713
670
  }
714
671
  }
715
672
  `;
716
-
717
- class LoopbackPermissionBlockedModal extends EmbeddedModal {
718
- contentStyles = css$2;
719
- get contentHtml() {
720
- const instructions = getIsPwaLaunchedAsApp()
721
- ? 'Long press the app icon on your home screen to open site settings'
722
- : 'Tap the lock or settings icon in the address bar to open site settings';
723
- return ErrorDialogHtml$2.replace('{{PERMISSION_INSTRUCTION_DETAIL}}', instructions);
724
- }
725
- async init() {
726
- super.init();
727
- this.#prepareLaunchAction();
728
- }
729
- #prepareLaunchAction() {
730
- const launchButton = this.dom?.getElementById("mobile-wallet-adapter-launch-action");
731
- const listener = async (event) => {
732
- launchButton?.removeEventListener('click', listener);
733
- this.close(event);
734
- };
735
- launchButton?.addEventListener('click', listener);
736
- }
737
- }
673
+ //#endregion
674
+ //#region src/embedded-modal/loopbackBlockedModal.ts
675
+ var LoopbackPermissionBlockedModal = class extends EmbeddedModal {
676
+ contentStyles = css$2;
677
+ get contentHtml() {
678
+ const instructions = getIsPwaLaunchedAsApp() ? "Long press the app icon on your home screen to open site settings" : "Tap the lock or settings icon in the address bar to open site settings";
679
+ return ErrorDialogHtml$2.replace("{{PERMISSION_INSTRUCTION_DETAIL}}", instructions);
680
+ }
681
+ async init() {
682
+ super.init();
683
+ this.#prepareLaunchAction();
684
+ }
685
+ #prepareLaunchAction() {
686
+ const launchButton = this.dom?.getElementById("mobile-wallet-adapter-launch-action");
687
+ const listener = async (event) => {
688
+ launchButton?.removeEventListener("click", listener);
689
+ this.close(event);
690
+ };
691
+ launchButton?.addEventListener("click", listener);
692
+ }
693
+ };
738
694
  const ErrorDialogHtml$2 = `
739
695
  <div class="mobile-wallet-adapter-embedded-modal-header">
740
696
  Local Wallet Connection
@@ -875,28 +831,27 @@ const css$2 = `
875
831
  }
876
832
  }
877
833
  `;
878
-
879
- class LoopbackPermissionModal extends EmbeddedModal {
880
- contentStyles = css$1;
881
- contentHtml = ErrorDialogHtml$1;
882
- async init() {
883
- super.init();
884
- this.#prepareLaunchAction();
885
- }
886
- #prepareLaunchAction() {
887
- const launchButton = this.dom?.getElementById("mobile-wallet-adapter-launch-action");
888
- const listener = async () => {
889
- launchButton?.removeEventListener('click', listener);
890
- try {
891
- // Trigger LNA permission prompting
892
- await fetch('http://localhost');
893
- }
894
- catch (e) { /* Ignore errors from fetch */ }
895
- this.close();
896
- };
897
- launchButton?.addEventListener('click', listener);
898
- }
899
- }
834
+ //#endregion
835
+ //#region src/embedded-modal/loopbackPermissionModal.ts
836
+ var LoopbackPermissionModal = class extends EmbeddedModal {
837
+ contentStyles = css$1;
838
+ contentHtml = ErrorDialogHtml$1;
839
+ async init() {
840
+ super.init();
841
+ this.#prepareLaunchAction();
842
+ }
843
+ #prepareLaunchAction() {
844
+ const launchButton = this.dom?.getElementById("mobile-wallet-adapter-launch-action");
845
+ const listener = async () => {
846
+ launchButton?.removeEventListener("click", listener);
847
+ try {
848
+ await fetch("http://localhost");
849
+ } catch {}
850
+ this.close();
851
+ };
852
+ launchButton?.addEventListener("click", listener);
853
+ }
854
+ };
900
855
  const ErrorDialogHtml$1 = `
901
856
  <div class="mobile-wallet-adapter-embedded-modal-title">Allow connections to your wallet</div>
902
857
  <div id="mobile-wallet-adapter-local-launch-message" class="mobile-wallet-adapter-embedded-modal-subtitle">
@@ -970,1015 +925,842 @@ const css$1 = `
970
925
  }
971
926
  }
972
927
  `;
973
-
928
+ //#endregion
929
+ //#region src/getIsSupported.ts
974
930
  function getIsLocalAssociationSupported() {
975
- return (typeof window !== 'undefined' &&
976
- window.isSecureContext &&
977
- typeof document !== 'undefined' &&
978
- /android/i.test(navigator.userAgent));
931
+ return typeof window !== "undefined" && window.isSecureContext && typeof document !== "undefined" && /android/i.test(navigator.userAgent);
979
932
  }
980
933
  function getIsRemoteAssociationSupported() {
981
- return (typeof window !== 'undefined' &&
982
- window.isSecureContext &&
983
- typeof document !== 'undefined' &&
984
- !/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent));
934
+ return typeof window !== "undefined" && window.isSecureContext && typeof document !== "undefined" && !/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
985
935
  }
986
- // Source: https://github.com/anza-xyz/wallet-adapter/blob/master/packages/core/react/src/getEnvironment.ts#L14
987
- // This is the same implementation that gated MWA in the Anza wallet-adapter-react library.
988
936
  function isWebView(userAgentString) {
989
- return /(WebView|Version\/.+(Chrome)\/(\d+)\.(\d+)\.(\d+)\.(\d+)|; wv\).+(Chrome)\/(\d+)\.(\d+)\.(\d+)\.(\d+))/i.test(userAgentString);
937
+ return /(WebView|Version\/.+(Chrome)\/(\d+)\.(\d+)\.(\d+)\.(\d+)|; wv\).+(Chrome)\/(\d+)\.(\d+)\.(\d+)\.(\d+))/i.test(userAgentString);
938
+ }
939
+ function isSolanaMobileWebShell(userAgentString) {
940
+ return userAgentString.includes("Solana Mobile Web Shell");
990
941
  }
991
- // Source: https://web.dev/learn/pwa/detection/
992
942
  function getIsPwaLaunchedAsApp() {
993
- // Check for Android TWA
994
- const isAndroidTwa = typeof document !== 'undefined' && document.referrer.startsWith('android-app://');
995
- // Check for display-mode: standalone, fullscreen, or minimal-ui
996
- if (typeof window == 'undefined')
997
- return isAndroidTwa;
998
- const isStandalone = window.matchMedia('(display-mode: standalone)').matches;
999
- const isFullscreen = window.matchMedia('(display-mode: fullscreen)').matches;
1000
- const isMinimalUI = window.matchMedia('(display-mode: minimal-ui)').matches;
1001
- // App mode if any of these conditions are true
1002
- return isAndroidTwa || isStandalone || isFullscreen || isMinimalUI;
943
+ const isAndroidTwa = typeof document !== "undefined" && document.referrer.startsWith("android-app://");
944
+ if (typeof window == "undefined") return isAndroidTwa;
945
+ const isStandalone = window.matchMedia("(display-mode: standalone)").matches;
946
+ const isFullscreen = window.matchMedia("(display-mode: fullscreen)").matches;
947
+ const isMinimalUI = window.matchMedia("(display-mode: minimal-ui)").matches;
948
+ return isAndroidTwa || isStandalone || isFullscreen || isMinimalUI;
1003
949
  }
1004
950
  async function checkLocalNetworkAccessPermission() {
1005
- try {
1006
- let lnaPermission = await navigator.permissions.query({ name: "loopback-network" });
1007
- if (lnaPermission.state === "granted") {
1008
- // LNA permission already granted, continuing
1009
- return;
1010
- }
1011
- else if (lnaPermission.state === "denied") {
1012
- // LNA permission denied, aborting
1013
- const modal = new LoopbackPermissionBlockedModal();
1014
- modal.init();
1015
- modal.open();
1016
- throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_LOOPBACK_ACCESS_BLOCKED, 'Local Network Access permission denied');
1017
- }
1018
- else if (lnaPermission.state === "prompt") {
1019
- // Show permission explainer to user, and wait for the permission to change
1020
- const modal = new LoopbackPermissionModal();
1021
- const updatedState = await new Promise((resolve, reject) => {
1022
- modal.addEventListener('close', (event) => {
1023
- if (event) {
1024
- reject(new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_ASSOCIATION_CANCELLED, 'Wallet connection cancelled by user', { event }));
1025
- }
1026
- });
1027
- lnaPermission.onchange = () => {
1028
- lnaPermission.onchange = null; // cleanup
1029
- resolve(lnaPermission.state);
1030
- };
1031
- modal.init();
1032
- modal.open();
1033
- });
1034
- if (updatedState === "granted") {
1035
- // User has granted the permission, now we need another click to continue
1036
- // Note: this is required to avoid being blocked by the browsers pop-up blocker
1037
- const modal = new LocalConnectionModal();
1038
- await new Promise((resolve, reject) => {
1039
- modal.addEventListener('close', (event) => {
1040
- if (event) {
1041
- reject(new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_ASSOCIATION_CANCELLED, 'Wallet connection cancelled by user', { event }));
1042
- }
1043
- });
1044
- modal.initWithCallback(async () => {
1045
- resolve(true);
1046
- });
1047
- modal.open();
1048
- });
1049
- return;
1050
- }
1051
- else {
1052
- // recurse, to avoid duplicating above logic
1053
- return await checkLocalNetworkAccessPermission();
1054
- }
1055
- }
1056
- // Shouldn't ever get here
1057
- throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_LOOPBACK_ACCESS_BLOCKED, 'Local Network Access permission unknown');
1058
- }
1059
- catch (e) {
1060
- if (e instanceof TypeError &&
1061
- (e.message.includes('loopback-network') ||
1062
- e.message.includes('local-network-access'))) {
1063
- // LNA permission API not found, continuing
1064
- return;
1065
- }
1066
- // Re-throw existing adapter errors as-is
1067
- if (e instanceof SolanaMobileWalletAdapterError) {
1068
- throw e;
1069
- }
1070
- // An unknown error occurred, wrap it
1071
- throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_LOOPBACK_ACCESS_BLOCKED, e instanceof Error ? e.message : 'Local Network Access permission unknown');
1072
- }
1073
- }
1074
-
1075
- const SolanaMobileWalletAdapterWalletName = 'Mobile Wallet Adapter';
1076
- const SolanaMobileWalletAdapterRemoteWalletName = 'Remote Mobile Wallet Adapter';
951
+ if (typeof navigator !== "undefined" && isSolanaMobileWebShell(navigator.userAgent)) return;
952
+ try {
953
+ const lnaPermission = await navigator.permissions.query({ name: "loopback-network" });
954
+ if (lnaPermission.state === "granted") return;
955
+ else if (lnaPermission.state === "denied") {
956
+ const modal = new LoopbackPermissionBlockedModal();
957
+ modal.init();
958
+ modal.open();
959
+ throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_LOOPBACK_ACCESS_BLOCKED, "Local Network Access permission denied");
960
+ } else if (lnaPermission.state === "prompt") {
961
+ const modal = new LoopbackPermissionModal();
962
+ if (await new Promise((resolve, reject) => {
963
+ modal.addEventListener("close", (event) => {
964
+ if (event) reject(new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_ASSOCIATION_CANCELLED, "Wallet connection cancelled by user", { event }));
965
+ });
966
+ lnaPermission.onchange = () => {
967
+ lnaPermission.onchange = null;
968
+ resolve(lnaPermission.state);
969
+ };
970
+ modal.init();
971
+ modal.open();
972
+ }) === "granted") {
973
+ const modal = new LocalConnectionModal();
974
+ await new Promise((resolve, reject) => {
975
+ modal.addEventListener("close", (event) => {
976
+ if (event) reject(new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_ASSOCIATION_CANCELLED, "Wallet connection cancelled by user", { event }));
977
+ });
978
+ modal.initWithCallback(async () => {
979
+ resolve(true);
980
+ });
981
+ modal.open();
982
+ });
983
+ return;
984
+ } else return await checkLocalNetworkAccessPermission();
985
+ }
986
+ throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_LOOPBACK_ACCESS_BLOCKED, "Local Network Access permission unknown");
987
+ } catch (e) {
988
+ if (e instanceof TypeError && (e.message.includes("loopback-network") || e.message.includes("local-network-access"))) return;
989
+ if (e instanceof SolanaMobileWalletAdapterError) throw e;
990
+ throw new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_LOOPBACK_ACCESS_BLOCKED, e instanceof Error ? e.message : "Local Network Access permission unknown");
991
+ }
992
+ }
993
+ //#endregion
994
+ //#region src/icon.ts
995
+ const icon = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03IDIuNUgxN0MxNy44Mjg0IDIuNSAxOC41IDMuMTcxNTcgMTguNSA0VjIwQzE4LjUgMjAuODI4NCAxNy44Mjg0IDIxLjUgMTcgMjEuNUg3QzYuMTcxNTcgMjEuNSA1LjUgMjAuODI4NCA1LjUgMjBWNEM1LjUgMy4xNzE1NyA2LjE3MTU3IDIuNSA3IDIuNVpNMyA0QzMgMS43OTA4NiA0Ljc5MDg2IDAgNyAwSDE3QzE5LjIwOTEgMCAyMSAxLjc5MDg2IDIxIDRWMjBDMjEgMjIuMjA5MSAxOS4yMDkxIDI0IDE3IDI0SDdDNC43OTA4NiAyNCAzIDIyLjIwOTEgMyAyMFY0Wk0xMSA0LjYxNTM4QzEwLjQ0NzcgNC42MTUzOCAxMCA1LjA2MzEgMTAgNS42MTUzOFY2LjM4NDYyQzEwIDYuOTM2OSAxMC40NDc3IDcuMzg0NjIgMTEgNy4zODQ2MkgxM0MxMy41NTIzIDcuMzg0NjIgMTQgNi45MzY5IDE0IDYuMzg0NjJWNS42MTUzOEMxNCA1LjA2MzEgMTMuNTUyMyA0LjYxNTM4IDEzIDQuNjE1MzhIMTFaIiBmaWxsPSIjRENCOEZGIi8+Cjwvc3ZnPgo=";
996
+ //#endregion
997
+ //#region src/wallet.ts
998
+ const SolanaMobileWalletAdapterWalletName = "Mobile Wallet Adapter";
999
+ const SolanaMobileWalletAdapterRemoteWalletName = "Remote Mobile Wallet Adapter";
1077
1000
  const SIGNATURE_LENGTH_IN_BYTES = 64;
1078
- const DEFAULT_FEATURES = [SolanaSignAndSendTransaction, SolanaSignTransaction, SolanaSignMessage, SolanaSignIn];
1079
- const WALLET_ASSOCIATION_TIMEOUT = 30_000;
1080
- class LocalSolanaMobileWalletAdapterWallet {
1081
- #listeners = {};
1082
- #version = '1.0.0'; // wallet-standard version
1083
- #name = SolanaMobileWalletAdapterWalletName;
1084
- #url = 'https://solanamobile.com/wallets';
1085
- #icon = icon;
1086
- #appIdentity;
1087
- #authorization;
1088
- #authorizationCache;
1089
- #connecting = false;
1090
- /**
1091
- * Every time the connection is recycled in some way (eg. `disconnect()` is called)
1092
- * increment this and use it to make sure that `transact` calls from the previous
1093
- * 'generation' don't continue to do work and throw exceptions.
1094
- */
1095
- #connectionGeneration = 0;
1096
- #chains = [];
1097
- #chainSelector;
1098
- #optionalFeatures;
1099
- #onWalletNotFound;
1100
- get version() {
1101
- return this.#version;
1102
- }
1103
- get name() {
1104
- return this.#name;
1105
- }
1106
- get url() {
1107
- return this.#url;
1108
- }
1109
- get icon() {
1110
- return this.#icon;
1111
- }
1112
- get chains() {
1113
- return this.#chains;
1114
- }
1115
- get features() {
1116
- return {
1117
- [StandardConnect]: {
1118
- version: '1.0.0',
1119
- connect: this.#connect,
1120
- },
1121
- [StandardDisconnect]: {
1122
- version: '1.0.0',
1123
- disconnect: this.#disconnect,
1124
- },
1125
- [StandardEvents]: {
1126
- version: '1.0.0',
1127
- on: this.#on,
1128
- },
1129
- [SolanaSignMessage]: {
1130
- version: '1.0.0',
1131
- signMessage: this.#signMessage,
1132
- },
1133
- [SolanaSignIn]: {
1134
- version: '1.0.0',
1135
- signIn: this.#signIn,
1136
- },
1137
- ...this.#optionalFeatures,
1138
- };
1139
- }
1140
- get accounts() {
1141
- return this.#authorization?.accounts ?? [];
1142
- }
1143
- constructor(config) {
1144
- this.#authorizationCache = config.authorizationCache;
1145
- this.#appIdentity = config.appIdentity;
1146
- this.#chains = config.chains;
1147
- this.#chainSelector = config.chainSelector;
1148
- this.#onWalletNotFound = config.onWalletNotFound;
1149
- this.#optionalFeatures = {
1150
- // In MWA 1.0, signAndSend is optional and signTransaction is mandatory. Whereas in MWA 2.0+,
1151
- // signAndSend is mandatory and signTransaction is optional (and soft deprecated). As of mid
1152
- // 2025, all MWA wallets support both signAndSendTransaction and signTransaction so its safe
1153
- // assume both are supported here. The features will be updated based on the actual connected
1154
- // wallets capabilities during connection regardless, so this is safe.
1155
- [SolanaSignAndSendTransaction]: {
1156
- version: '1.0.0',
1157
- supportedTransactionVersions: ['legacy', 0],
1158
- signAndSendTransaction: this.#signAndSendTransaction,
1159
- },
1160
- [SolanaSignTransaction]: {
1161
- version: '1.0.0',
1162
- supportedTransactionVersions: ['legacy', 0],
1163
- signTransaction: this.#signTransaction,
1164
- },
1165
- };
1166
- }
1167
- get connected() {
1168
- return !!this.#authorization;
1169
- }
1170
- get isAuthorized() {
1171
- return !!this.#authorization;
1172
- }
1173
- get currentAuthorization() {
1174
- return this.#authorization;
1175
- }
1176
- get cachedAuthorizationResult() {
1177
- return this.#authorizationCache.get();
1178
- }
1179
- #on = (event, listener) => {
1180
- this.#listeners[event]?.push(listener) || (this.#listeners[event] = [listener]);
1181
- return () => this.#off(event, listener);
1182
- };
1183
- #emit(event, ...args) {
1184
- // eslint-disable-next-line prefer-spread
1185
- this.#listeners[event]?.forEach((listener) => listener.apply(null, args));
1186
- }
1187
- #off(event, listener) {
1188
- this.#listeners[event] = this.#listeners[event]?.filter((existingListener) => listener !== existingListener);
1189
- }
1190
- #connect = async ({ silent } = {}) => {
1191
- if (this.#connecting || this.connected) {
1192
- return { accounts: this.accounts };
1193
- }
1194
- this.#connecting = true;
1195
- try {
1196
- if (silent) {
1197
- const cachedAuthorization = await this.#authorizationCache.get();
1198
- if (cachedAuthorization) {
1199
- await this.#handleWalletCapabilitiesResult(cachedAuthorization.capabilities);
1200
- await this.#handleAuthorizationResult(cachedAuthorization);
1201
- }
1202
- else {
1203
- return { accounts: this.accounts };
1204
- }
1205
- }
1206
- else {
1207
- await this.#performAuthorization();
1208
- }
1209
- }
1210
- catch (e) {
1211
- throw new Error((e instanceof Error && e.message) || 'Unknown error');
1212
- }
1213
- finally {
1214
- this.#connecting = false;
1215
- }
1216
- return { accounts: this.accounts };
1217
- };
1218
- #performAuthorization = async (signInPayload) => {
1219
- try {
1220
- const cachedAuthorizationResult = await this.#authorizationCache.get();
1221
- if (cachedAuthorizationResult) {
1222
- // TODO: Evaluate whether there's any threat to not `awaiting` this expression
1223
- this.#handleAuthorizationResult(cachedAuthorizationResult);
1224
- return cachedAuthorizationResult;
1225
- }
1226
- const selectedChain = await this.#chainSelector.select(this.#chains);
1227
- return await this.#transact(async (wallet) => {
1228
- const [capabilities, mwaAuthorizationResult] = await Promise.all([
1229
- wallet.getCapabilities(),
1230
- wallet.authorize({
1231
- chain: selectedChain,
1232
- identity: this.#appIdentity,
1233
- sign_in_payload: signInPayload,
1234
- })
1235
- ]);
1236
- const accounts = this.#accountsToWalletStandardAccounts(mwaAuthorizationResult.accounts);
1237
- const authorization = { ...mwaAuthorizationResult,
1238
- accounts, chain: selectedChain, capabilities: capabilities };
1239
- // TODO: Evaluate whether there's any threat to not `awaiting` this expression
1240
- Promise.all([
1241
- this.#handleWalletCapabilitiesResult(capabilities),
1242
- this.#authorizationCache.set(authorization),
1243
- this.#handleAuthorizationResult(authorization),
1244
- ]);
1245
- return authorization;
1246
- });
1247
- }
1248
- catch (e) {
1249
- throw new Error((e instanceof Error && e.message) || 'Unknown error');
1250
- }
1251
- };
1252
- #handleAuthorizationResult = async (authorization) => {
1253
- const didPublicKeysChange =
1254
- // Case 1: We started from having no authorization.
1255
- this.#authorization == null ||
1256
- // Case 2: The number of authorized accounts changed.
1257
- this.#authorization?.accounts.length !== authorization.accounts.length ||
1258
- // Case 3: The new list of addresses isn't exactly the same as the old list, in the same order.
1259
- this.#authorization.accounts.some((account, ii) => account.address !== authorization.accounts[ii].address);
1260
- this.#authorization = authorization;
1261
- if (didPublicKeysChange) {
1262
- this.#emit('change', { accounts: this.accounts });
1263
- }
1264
- };
1265
- #handleWalletCapabilitiesResult = async (capabilities) => {
1266
- // TODO: investigate why using SolanaSignTransactions constant breaks treeshaking
1267
- const supportsSignTransaction = capabilities.features.includes('solana:signTransactions'); //SolanaSignTransactions);
1268
- const supportsSignAndSendTransaction = capabilities.supports_sign_and_send_transactions;
1269
- const didCapabilitiesChange = SolanaSignAndSendTransaction in this.features !== supportsSignAndSendTransaction ||
1270
- SolanaSignTransaction in this.features !== supportsSignTransaction;
1271
- this.#optionalFeatures = {
1272
- ...((supportsSignAndSendTransaction || (!supportsSignAndSendTransaction && !supportsSignTransaction)) && {
1273
- [SolanaSignAndSendTransaction]: {
1274
- version: '1.0.0',
1275
- supportedTransactionVersions: ['legacy', 0],
1276
- signAndSendTransaction: this.#signAndSendTransaction,
1277
- },
1278
- }),
1279
- ...(supportsSignTransaction && {
1280
- [SolanaSignTransaction]: {
1281
- version: '1.0.0',
1282
- supportedTransactionVersions: ['legacy', 0],
1283
- signTransaction: this.#signTransaction,
1284
- },
1285
- }),
1286
- };
1287
- if (didCapabilitiesChange) {
1288
- this.#emit('change', { features: this.features });
1289
- }
1290
- };
1291
- #performReauthorization = async (wallet, authToken, chain) => {
1292
- try {
1293
- const [capabilities, mwaAuthorizationResult] = await Promise.all([
1294
- this.#authorization?.capabilities ?? await wallet.getCapabilities(),
1295
- wallet.authorize({
1296
- auth_token: authToken,
1297
- identity: this.#appIdentity,
1298
- chain: chain
1299
- })
1300
- ]);
1301
- const accounts = this.#accountsToWalletStandardAccounts(mwaAuthorizationResult.accounts);
1302
- const authorization = { ...mwaAuthorizationResult,
1303
- accounts: accounts, chain: chain, capabilities: capabilities
1304
- };
1305
- // TODO: Evaluate whether there's any threat to not `awaiting` this expression
1306
- Promise.all([
1307
- this.#authorizationCache.set(authorization),
1308
- this.#handleAuthorizationResult(authorization),
1309
- ]);
1310
- }
1311
- catch (e) {
1312
- this.#disconnect();
1313
- throw new Error((e instanceof Error && e.message) || 'Unknown error');
1314
- }
1315
- };
1316
- #disconnect = async () => {
1317
- this.#authorizationCache.clear(); // TODO: Evaluate whether there's any threat to not `awaiting` this expression
1318
- this.#connecting = false;
1319
- this.#connectionGeneration++;
1320
- this.#authorization = undefined;
1321
- this.#emit('change', { accounts: this.accounts });
1322
- };
1323
- #transact = async (callback) => {
1324
- const walletUriBase = this.#authorization?.wallet_uri_base;
1325
- const config = walletUriBase ? { baseUri: walletUriBase } : undefined;
1326
- const currentConnectionGeneration = this.#connectionGeneration;
1327
- const loadingSpinner = new EmbeddedLoadingSpinner();
1328
- try {
1329
- // check that we have permissions for local app connections, then run
1330
- // wallet association (transact). In case the user manually cancels
1331
- // the wallet association, cancel the connection after a timeout.
1332
- let associating = true;
1333
- let timeout = undefined;
1334
- const result = await Promise.race([
1335
- checkLocalNetworkAccessPermission()
1336
- .then(async () => {
1337
- // Begin local connection, show loading spinner while we connect
1338
- loadingSpinner.init();
1339
- const { wallet, close } = await startScenario(config);
1340
- loadingSpinner.addEventListener('close', (event) => { if (event)
1341
- close(); });
1342
- loadingSpinner.open();
1343
- const result = await callback(await wallet);
1344
- loadingSpinner.close();
1345
- close();
1346
- return result;
1347
- }),
1348
- new Promise((_, reject) => {
1349
- timeout = setTimeout(() => {
1350
- if (associating) { // only timeout during association
1351
- reject(new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_ASSOCIATION_CANCELLED, 'Wallet connection timed out', { event: undefined }));
1352
- }
1353
- }, WALLET_ASSOCIATION_TIMEOUT);
1354
- })
1355
- ]);
1356
- clearTimeout(timeout);
1357
- return result;
1358
- }
1359
- catch (e) {
1360
- loadingSpinner.close();
1361
- if (this.#connectionGeneration !== currentConnectionGeneration) {
1362
- await new Promise(() => { }); // Never resolve.
1363
- }
1364
- if (e instanceof Error &&
1365
- e.name === 'SolanaMobileWalletAdapterError' &&
1366
- e.code === 'ERROR_WALLET_NOT_FOUND') {
1367
- await this.#onWalletNotFound(this);
1368
- }
1369
- throw e;
1370
- }
1371
- };
1372
- #assertIsAuthorized = () => {
1373
- if (!this.#authorization)
1374
- throw new Error('Wallet not connected');
1375
- return { authToken: this.#authorization.auth_token, chain: this.#authorization.chain };
1376
- };
1377
- #accountsToWalletStandardAccounts = (accounts) => {
1378
- return accounts.map((account) => {
1379
- const publicKey = toUint8Array(account.address);
1380
- return {
1381
- address: base58.encode(publicKey),
1382
- publicKey,
1383
- label: account.label,
1384
- icon: account.icon,
1385
- chains: account.chains ?? this.#chains,
1386
- // TODO: get supported features from getCapabilities API
1387
- features: account.features ?? DEFAULT_FEATURES
1388
- };
1389
- });
1390
- };
1391
- #performSignTransactions = async (transactions) => {
1392
- const { authToken, chain } = this.#assertIsAuthorized();
1393
- try {
1394
- const base64Transactions = transactions.map((tx) => { return fromUint8Array(tx); });
1395
- return await this.#transact(async (wallet) => {
1396
- await this.#performReauthorization(wallet, authToken, chain);
1397
- const signedTransactions = (await wallet.signTransactions({
1398
- payloads: base64Transactions,
1399
- })).signed_payloads.map(toUint8Array);
1400
- return signedTransactions;
1401
- });
1402
- }
1403
- catch (e) {
1404
- throw new Error((e instanceof Error && e.message) || 'Unknown error');
1405
- }
1406
- };
1407
- #performSignAndSendTransaction = async (transaction, options) => {
1408
- const { authToken, chain } = this.#assertIsAuthorized();
1409
- try {
1410
- return await this.#transact(async (wallet) => {
1411
- const [capabilities, _1] = await Promise.all([
1412
- wallet.getCapabilities(),
1413
- this.#performReauthorization(wallet, authToken, chain)
1414
- ]);
1415
- if (capabilities.supports_sign_and_send_transactions) {
1416
- const base64Transaction = fromUint8Array(transaction);
1417
- const signatures = (await wallet.signAndSendTransactions({
1418
- ...options,
1419
- payloads: [base64Transaction],
1420
- })).signatures.map(toUint8Array);
1421
- return signatures[0];
1422
- }
1423
- else {
1424
- throw new Error('connected wallet does not support signAndSendTransaction');
1425
- }
1426
- });
1427
- }
1428
- catch (e) {
1429
- throw new Error((e instanceof Error && e.message) || 'Unknown error');
1430
- }
1431
- };
1432
- #signAndSendTransaction = async (...inputs) => {
1433
- const outputs = [];
1434
- for (const input of inputs) {
1435
- const signature = await this.#performSignAndSendTransaction(input.transaction, input.options);
1436
- outputs.push({ signature });
1437
- }
1438
- return outputs;
1439
- };
1440
- #signTransaction = async (...inputs) => {
1441
- return (await this.#performSignTransactions(inputs.map(({ transaction }) => transaction)))
1442
- .map((signedTransaction) => {
1443
- return { signedTransaction };
1444
- });
1445
- };
1446
- #signMessage = async (...inputs) => {
1447
- const { authToken, chain } = this.#assertIsAuthorized();
1448
- const addresses = inputs.map(({ account }) => fromUint8Array(new Uint8Array(account.publicKey)));
1449
- const messages = inputs.map(({ message }) => fromUint8Array(message));
1450
- try {
1451
- return await this.#transact(async (wallet) => {
1452
- await this.#performReauthorization(wallet, authToken, chain);
1453
- const signedMessages = (await wallet.signMessages({
1454
- addresses: addresses,
1455
- payloads: messages,
1456
- })).signed_payloads.map(toUint8Array);
1457
- return signedMessages.map((signedMessage) => {
1458
- return { signedMessage: signedMessage, signature: signedMessage.slice(-SIGNATURE_LENGTH_IN_BYTES) };
1459
- });
1460
- });
1461
- }
1462
- catch (e) {
1463
- throw new Error((e instanceof Error && e.message) || 'Unknown error');
1464
- }
1465
- };
1466
- #signIn = async (...inputs) => {
1467
- const outputs = [];
1468
- if (inputs.length > 1) {
1469
- for (const input of inputs) {
1470
- outputs.push(await this.#performSignIn(input));
1471
- }
1472
- }
1473
- else {
1474
- return [await this.#performSignIn(inputs[0])];
1475
- }
1476
- return outputs;
1477
- };
1478
- #performSignIn = async (input) => {
1479
- this.#connecting = true;
1480
- try {
1481
- const authorizationResult = await this.#performAuthorization({
1482
- ...input,
1483
- domain: input?.domain ?? window.location.host
1484
- });
1485
- if (!authorizationResult.sign_in_result) {
1486
- throw new Error("Sign in failed, no sign in result returned by wallet");
1487
- }
1488
- const signedInAddress = authorizationResult.sign_in_result.address;
1489
- const signedInAccount = authorizationResult.accounts.find(acc => acc.address == signedInAddress);
1490
- return {
1491
- account: {
1492
- ...signedInAccount ?? {
1493
- address: base58.encode(toUint8Array(signedInAddress))
1494
- },
1495
- publicKey: toUint8Array(signedInAddress),
1496
- chains: signedInAccount?.chains ?? this.#chains,
1497
- features: signedInAccount?.features ?? authorizationResult.capabilities.features
1498
- },
1499
- signedMessage: toUint8Array(authorizationResult.sign_in_result.signed_message),
1500
- signature: toUint8Array(authorizationResult.sign_in_result.signature)
1501
- };
1502
- }
1503
- catch (e) {
1504
- throw new Error((e instanceof Error && e.message) || 'Unknown error');
1505
- }
1506
- finally {
1507
- this.#connecting = false;
1508
- }
1509
- };
1510
- }
1511
- class RemoteSolanaMobileWalletAdapterWallet {
1512
- #listeners = {};
1513
- #version = '1.0.0'; // wallet-standard version
1514
- #name = SolanaMobileWalletAdapterRemoteWalletName;
1515
- #url = 'https://solanamobile.com/wallets';
1516
- #icon = icon;
1517
- #appIdentity;
1518
- #authorization;
1519
- #authorizationCache;
1520
- #connecting = false;
1521
- /**
1522
- * Every time the connection is recycled in some way (eg. `disconnect()` is called)
1523
- * increment this and use it to make sure that `transact` calls from the previous
1524
- * 'generation' don't continue to do work and throw exceptions.
1525
- */
1526
- #connectionGeneration = 0;
1527
- #chains = [];
1528
- #chainSelector;
1529
- #optionalFeatures;
1530
- #onWalletNotFound;
1531
- #hostAuthority;
1532
- #session;
1533
- get version() {
1534
- return this.#version;
1535
- }
1536
- get name() {
1537
- return this.#name;
1538
- }
1539
- get url() {
1540
- return this.#url;
1541
- }
1542
- get icon() {
1543
- return this.#icon;
1544
- }
1545
- get chains() {
1546
- return this.#chains;
1547
- }
1548
- get features() {
1549
- return {
1550
- [StandardConnect]: {
1551
- version: '1.0.0',
1552
- connect: this.#connect,
1553
- },
1554
- [StandardDisconnect]: {
1555
- version: '1.0.0',
1556
- disconnect: this.#disconnect,
1557
- },
1558
- [StandardEvents]: {
1559
- version: '1.0.0',
1560
- on: this.#on,
1561
- },
1562
- [SolanaSignMessage]: {
1563
- version: '1.0.0',
1564
- signMessage: this.#signMessage,
1565
- },
1566
- [SolanaSignIn]: {
1567
- version: '1.0.0',
1568
- signIn: this.#signIn,
1569
- },
1570
- ...this.#optionalFeatures,
1571
- };
1572
- }
1573
- get accounts() {
1574
- return this.#authorization?.accounts ?? [];
1575
- }
1576
- constructor(config) {
1577
- this.#authorizationCache = config.authorizationCache;
1578
- this.#appIdentity = config.appIdentity;
1579
- this.#chains = config.chains;
1580
- this.#chainSelector = config.chainSelector;
1581
- this.#hostAuthority = config.remoteHostAuthority;
1582
- this.#onWalletNotFound = config.onWalletNotFound;
1583
- this.#optionalFeatures = {
1584
- // In MWA 1.0, signAndSend is optional and signTransaction is mandatory. Whereas in MWA 2.0+,
1585
- // signAndSend is mandatory and signTransaction is optional (and soft deprecated). As of mid
1586
- // 2025, all MWA wallets support both signAndSendTransaction and signTransaction so its safe
1587
- // assume both are supported here. The features will be updated based on the actual connected
1588
- // wallets capabilities during connection regardless, so this is safe.
1589
- [SolanaSignAndSendTransaction]: {
1590
- version: '1.0.0',
1591
- supportedTransactionVersions: ['legacy', 0],
1592
- signAndSendTransaction: this.#signAndSendTransaction,
1593
- },
1594
- [SolanaSignTransaction]: {
1595
- version: '1.0.0',
1596
- supportedTransactionVersions: ['legacy', 0],
1597
- signTransaction: this.#signTransaction,
1598
- },
1599
- };
1600
- }
1601
- get connected() {
1602
- return !!this.#session && !!this.#authorization;
1603
- }
1604
- get isAuthorized() {
1605
- return !!this.#authorization;
1606
- }
1607
- get currentAuthorization() {
1608
- return this.#authorization;
1609
- }
1610
- get cachedAuthorizationResult() {
1611
- return this.#authorizationCache.get();
1612
- }
1613
- #on = (event, listener) => {
1614
- this.#listeners[event]?.push(listener) || (this.#listeners[event] = [listener]);
1615
- return () => this.#off(event, listener);
1616
- };
1617
- #emit(event, ...args) {
1618
- // eslint-disable-next-line prefer-spread
1619
- this.#listeners[event]?.forEach((listener) => listener.apply(null, args));
1620
- }
1621
- #off(event, listener) {
1622
- this.#listeners[event] = this.#listeners[event]?.filter((existingListener) => listener !== existingListener);
1623
- }
1624
- #connect = async ({ silent } = {}) => {
1625
- if (this.#connecting || this.connected) {
1626
- return { accounts: this.accounts };
1627
- }
1628
- this.#connecting = true;
1629
- try {
1630
- await this.#performAuthorization();
1631
- }
1632
- catch (e) {
1633
- throw new Error((e instanceof Error && e.message) || 'Unknown error');
1634
- }
1635
- finally {
1636
- this.#connecting = false;
1637
- }
1638
- return { accounts: this.accounts };
1639
- };
1640
- #performAuthorization = async (signInPayload) => {
1641
- try {
1642
- const cachedAuthorizationResult = await this.#authorizationCache.get();
1643
- if (cachedAuthorizationResult) {
1644
- // TODO: Evaluate whether there's any threat to not `awaiting` this expression
1645
- this.#handleAuthorizationResult(cachedAuthorizationResult);
1646
- return cachedAuthorizationResult;
1647
- }
1648
- if (this.#session)
1649
- this.#session = undefined;
1650
- const selectedChain = await this.#chainSelector.select(this.#chains);
1651
- return await this.#transact(async (wallet) => {
1652
- const [capabilities, mwaAuthorizationResult] = await Promise.all([
1653
- wallet.getCapabilities(),
1654
- wallet.authorize({
1655
- chain: selectedChain,
1656
- identity: this.#appIdentity,
1657
- sign_in_payload: signInPayload,
1658
- })
1659
- ]);
1660
- const accounts = this.#accountsToWalletStandardAccounts(mwaAuthorizationResult.accounts);
1661
- const authorizationResult = { ...mwaAuthorizationResult,
1662
- accounts, chain: selectedChain, capabilities: capabilities };
1663
- // TODO: Evaluate whether there's any threat to not `awaiting` this expression
1664
- Promise.all([
1665
- this.#handleWalletCapabilitiesResult(capabilities),
1666
- this.#authorizationCache.set(authorizationResult),
1667
- this.#handleAuthorizationResult(authorizationResult),
1668
- ]);
1669
- return authorizationResult;
1670
- });
1671
- }
1672
- catch (e) {
1673
- throw new Error((e instanceof Error && e.message) || 'Unknown error');
1674
- }
1675
- };
1676
- #handleAuthorizationResult = async (authorization) => {
1677
- const didPublicKeysChange =
1678
- // Case 1: We started from having no authorization.
1679
- this.#authorization == null ||
1680
- // Case 2: The number of authorized accounts changed.
1681
- this.#authorization?.accounts.length !== authorization.accounts.length ||
1682
- // Case 3: The new list of addresses isn't exactly the same as the old list, in the same order.
1683
- this.#authorization.accounts.some((account, ii) => account.address !== authorization.accounts[ii].address);
1684
- this.#authorization = authorization;
1685
- if (didPublicKeysChange) {
1686
- this.#emit('change', { accounts: this.accounts });
1687
- }
1688
- };
1689
- #handleWalletCapabilitiesResult = async (capabilities) => {
1690
- // TODO: investigate why using SolanaSignTransactions constant breaks treeshaking
1691
- const supportsSignTransaction = capabilities.features.includes('solana:signTransactions'); //SolanaSignTransactions);
1692
- const supportsSignAndSendTransaction = capabilities.supports_sign_and_send_transactions ||
1693
- capabilities.features.includes('solana:signAndSendTransaction');
1694
- const didCapabilitiesChange = SolanaSignAndSendTransaction in this.features !== supportsSignAndSendTransaction ||
1695
- SolanaSignTransaction in this.features !== supportsSignTransaction;
1696
- this.#optionalFeatures = {
1697
- ...(supportsSignAndSendTransaction && {
1698
- [SolanaSignAndSendTransaction]: {
1699
- version: '1.0.0',
1700
- supportedTransactionVersions: capabilities.supported_transaction_versions,
1701
- signAndSendTransaction: this.#signAndSendTransaction,
1702
- },
1703
- }),
1704
- ...(supportsSignTransaction && {
1705
- [SolanaSignTransaction]: {
1706
- version: '1.0.0',
1707
- supportedTransactionVersions: capabilities.supported_transaction_versions,
1708
- signTransaction: this.#signTransaction,
1709
- },
1710
- }),
1711
- };
1712
- if (didCapabilitiesChange) {
1713
- this.#emit('change', { features: this.features });
1714
- }
1715
- };
1716
- #performReauthorization = async (wallet, authToken, chain) => {
1717
- try {
1718
- const [capabilities, mwaAuthorizationResult] = await Promise.all([
1719
- this.#authorization?.capabilities ?? await wallet.getCapabilities(),
1720
- wallet.authorize({
1721
- auth_token: authToken,
1722
- identity: this.#appIdentity,
1723
- chain: chain
1724
- })
1725
- ]);
1726
- const accounts = this.#accountsToWalletStandardAccounts(mwaAuthorizationResult.accounts);
1727
- const authorization = { ...mwaAuthorizationResult,
1728
- accounts: accounts, chain: chain, capabilities: capabilities };
1729
- // TODO: Evaluate whether there's any threat to not `awaiting` this expression
1730
- Promise.all([
1731
- this.#authorizationCache.set(authorization),
1732
- this.#handleAuthorizationResult(authorization),
1733
- ]);
1734
- }
1735
- catch (e) {
1736
- this.#disconnect();
1737
- throw new Error((e instanceof Error && e.message) || 'Unknown error');
1738
- }
1739
- };
1740
- #disconnect = async () => {
1741
- this.#session?.close();
1742
- this.#authorizationCache.clear(); // TODO: Evaluate whether there's any threat to not `awaiting` this expression
1743
- this.#connecting = false;
1744
- this.#connectionGeneration++;
1745
- this.#authorization = undefined;
1746
- this.#session = undefined;
1747
- this.#emit('change', { accounts: this.accounts });
1748
- };
1749
- #transact = async (callback) => {
1750
- const walletUriBase = this.#authorization?.wallet_uri_base;
1751
- const baseConfig = walletUriBase ? { baseUri: walletUriBase } : undefined;
1752
- const remoteConfig = { ...baseConfig, remoteHostAuthority: this.#hostAuthority };
1753
- const currentConnectionGeneration = this.#connectionGeneration;
1754
- const modal = new RemoteConnectionModal();
1755
- if (this.#session) {
1756
- return callback(this.#session.wallet);
1757
- }
1758
- try {
1759
- // Begin remote connection, show modal with loading anim while we connect
1760
- modal.init();
1761
- modal.open();
1762
- const { associationUrl, close, wallet } = await startRemoteScenario(remoteConfig);
1763
- // Reflector is now connected, update the connection modal with qr code
1764
- const removeCloseListener = modal.addEventListener('close', (event) => {
1765
- if (event)
1766
- close();
1767
- });
1768
- modal.populateQRCode(associationUrl.toString());
1769
- // Wait for the wallet to be connected, then close the connection modal and proceed
1770
- this.#session = { close, wallet: await wallet };
1771
- removeCloseListener();
1772
- modal.close();
1773
- return await callback(this.#session.wallet);
1774
- }
1775
- catch (e) {
1776
- modal.close();
1777
- if (this.#connectionGeneration !== currentConnectionGeneration) {
1778
- await new Promise(() => { }); // Never resolve.
1779
- }
1780
- if (e instanceof Error &&
1781
- e.name === 'SolanaMobileWalletAdapterError' &&
1782
- e.code === 'ERROR_WALLET_NOT_FOUND') {
1783
- await this.#onWalletNotFound(this);
1784
- }
1785
- throw e;
1786
- }
1787
- };
1788
- #assertIsAuthorized = () => {
1789
- if (!this.#authorization)
1790
- throw new Error('Wallet not connected');
1791
- return { authToken: this.#authorization.auth_token, chain: this.#authorization.chain };
1792
- };
1793
- #accountsToWalletStandardAccounts = (accounts) => {
1794
- return accounts.map((account) => {
1795
- const publicKey = toUint8Array(account.address);
1796
- return {
1797
- address: base58.encode(publicKey),
1798
- publicKey,
1799
- label: account.label,
1800
- icon: account.icon,
1801
- chains: account.chains ?? this.#chains,
1802
- // TODO: get supported features from getCapabilities API
1803
- features: account.features ?? DEFAULT_FEATURES
1804
- };
1805
- });
1806
- };
1807
- #performSignTransactions = async (transactions) => {
1808
- const { authToken, chain } = this.#assertIsAuthorized();
1809
- try {
1810
- return await this.#transact(async (wallet) => {
1811
- await this.#performReauthorization(wallet, authToken, chain);
1812
- const signedTransactions = (await wallet.signTransactions({
1813
- payloads: transactions.map(fromUint8Array),
1814
- })).signed_payloads.map(toUint8Array);
1815
- return signedTransactions;
1816
- });
1817
- }
1818
- catch (e) {
1819
- throw new Error((e instanceof Error && e.message) || 'Unknown error');
1820
- }
1821
- };
1822
- #performSignAndSendTransaction = async (transaction, options) => {
1823
- const { authToken, chain } = this.#assertIsAuthorized();
1824
- try {
1825
- return await this.#transact(async (wallet) => {
1826
- const [capabilities, _1] = await Promise.all([
1827
- wallet.getCapabilities(),
1828
- this.#performReauthorization(wallet, authToken, chain)
1829
- ]);
1830
- if (capabilities.supports_sign_and_send_transactions) {
1831
- const signatures = (await wallet.signAndSendTransactions({
1832
- ...options,
1833
- payloads: [fromUint8Array(transaction)],
1834
- })).signatures.map(toUint8Array);
1835
- return signatures[0];
1836
- }
1837
- else {
1838
- throw new Error('connected wallet does not support signAndSendTransaction');
1839
- }
1840
- });
1841
- }
1842
- catch (e) {
1843
- throw new Error((e instanceof Error && e.message) || 'Unknown error');
1844
- }
1845
- };
1846
- #signAndSendTransaction = async (...inputs) => {
1847
- const outputs = [];
1848
- for (const input of inputs) {
1849
- const signature = (await this.#performSignAndSendTransaction(input.transaction, input.options));
1850
- outputs.push({ signature });
1851
- }
1852
- return outputs;
1853
- };
1854
- #signTransaction = async (...inputs) => {
1855
- return (await this.#performSignTransactions(inputs.map(({ transaction }) => transaction)))
1856
- .map((signedTransaction) => {
1857
- return { signedTransaction };
1858
- });
1859
- };
1860
- #signMessage = async (...inputs) => {
1861
- const { authToken, chain } = this.#assertIsAuthorized();
1862
- const addresses = inputs.map(({ account }) => fromUint8Array(new Uint8Array(account.publicKey)));
1863
- const messages = inputs.map(({ message }) => fromUint8Array(message));
1864
- try {
1865
- return await this.#transact(async (wallet) => {
1866
- await this.#performReauthorization(wallet, authToken, chain);
1867
- const signedMessages = (await wallet.signMessages({
1868
- addresses: addresses,
1869
- payloads: messages,
1870
- })).signed_payloads.map(toUint8Array);
1871
- return signedMessages.map((signedMessage) => {
1872
- return { signedMessage: signedMessage, signature: signedMessage.slice(-SIGNATURE_LENGTH_IN_BYTES) };
1873
- });
1874
- });
1875
- }
1876
- catch (e) {
1877
- throw new Error((e instanceof Error && e.message) || 'Unknown error');
1878
- }
1879
- };
1880
- #signIn = async (...inputs) => {
1881
- const outputs = [];
1882
- if (inputs.length > 1) {
1883
- for (const input of inputs) {
1884
- outputs.push(await this.#performSignIn(input));
1885
- }
1886
- }
1887
- else {
1888
- return [await this.#performSignIn(inputs[0])];
1889
- }
1890
- return outputs;
1891
- };
1892
- #performSignIn = async (input) => {
1893
- this.#connecting = true;
1894
- try {
1895
- const authorizationResult = await this.#performAuthorization({
1896
- ...input,
1897
- domain: input?.domain ?? window.location.host
1898
- });
1899
- if (!authorizationResult.sign_in_result) {
1900
- throw new Error("Sign in failed, no sign in result returned by wallet");
1901
- }
1902
- const signedInAddress = authorizationResult.sign_in_result.address;
1903
- const signedInAccount = authorizationResult.accounts.find(acc => acc.address == signedInAddress);
1904
- return {
1905
- account: {
1906
- ...signedInAccount ?? {
1907
- address: base58.encode(toUint8Array(signedInAddress))
1908
- },
1909
- publicKey: toUint8Array(signedInAddress),
1910
- chains: signedInAccount?.chains ?? this.#chains,
1911
- features: signedInAccount?.features ?? authorizationResult.capabilities.features
1912
- },
1913
- signedMessage: toUint8Array(authorizationResult.sign_in_result.signed_message),
1914
- signature: toUint8Array(authorizationResult.sign_in_result.signature)
1915
- };
1916
- }
1917
- catch (e) {
1918
- throw new Error((e instanceof Error && e.message) || 'Unknown error');
1919
- }
1920
- finally {
1921
- this.#connecting = false;
1922
- }
1923
- };
1924
- }
1925
-
1001
+ const DEFAULT_FEATURES = [
1002
+ SolanaSignAndSendTransaction,
1003
+ SolanaSignTransaction,
1004
+ SolanaSignMessage,
1005
+ SolanaSignIn
1006
+ ];
1007
+ const WALLET_ASSOCIATION_TIMEOUT = 3e4;
1008
+ function getErrorMessage(error) {
1009
+ return error instanceof Error ? error.message : "Unknown error";
1010
+ }
1011
+ var LocalSolanaMobileWalletAdapterWallet = class {
1012
+ #listeners = {};
1013
+ #version = "1.0.0";
1014
+ #name = SolanaMobileWalletAdapterWalletName;
1015
+ #url = "https://solanamobile.com/wallets";
1016
+ #icon = icon;
1017
+ #appIdentity;
1018
+ #authorization;
1019
+ #authorizationCache;
1020
+ #connecting = false;
1021
+ /**
1022
+ * Every time the connection is recycled in some way (eg. `disconnect()` is called)
1023
+ * increment this and use it to make sure that `transact` calls from the previous
1024
+ * 'generation' don't continue to do work and throw exceptions.
1025
+ */
1026
+ #connectionGeneration = 0;
1027
+ #chains = [];
1028
+ #chainSelector;
1029
+ #optionalFeatures;
1030
+ #onWalletNotFound;
1031
+ get version() {
1032
+ return this.#version;
1033
+ }
1034
+ get name() {
1035
+ return this.#name;
1036
+ }
1037
+ get url() {
1038
+ return this.#url;
1039
+ }
1040
+ get icon() {
1041
+ return this.#icon;
1042
+ }
1043
+ get chains() {
1044
+ return this.#chains;
1045
+ }
1046
+ get features() {
1047
+ return {
1048
+ [StandardConnect]: {
1049
+ version: "1.0.0",
1050
+ connect: this.#connect
1051
+ },
1052
+ [StandardDisconnect]: {
1053
+ version: "1.0.0",
1054
+ disconnect: this.#disconnect
1055
+ },
1056
+ [StandardEvents]: {
1057
+ version: "1.0.0",
1058
+ on: this.#on
1059
+ },
1060
+ [SolanaSignMessage]: {
1061
+ version: "1.0.0",
1062
+ signMessage: this.#signMessage
1063
+ },
1064
+ [SolanaSignIn]: {
1065
+ version: "1.0.0",
1066
+ signIn: this.#signIn
1067
+ },
1068
+ ...this.#optionalFeatures
1069
+ };
1070
+ }
1071
+ get accounts() {
1072
+ return this.#authorization?.accounts ?? [];
1073
+ }
1074
+ constructor(config) {
1075
+ this.#authorizationCache = config.authorizationCache;
1076
+ this.#appIdentity = config.appIdentity;
1077
+ this.#chains = config.chains;
1078
+ this.#chainSelector = config.chainSelector;
1079
+ this.#onWalletNotFound = config.onWalletNotFound;
1080
+ this.#optionalFeatures = {
1081
+ [SolanaSignAndSendTransaction]: {
1082
+ version: "1.0.0",
1083
+ supportedTransactionVersions: ["legacy", 0],
1084
+ signAndSendTransaction: this.#signAndSendTransaction
1085
+ },
1086
+ [SolanaSignTransaction]: {
1087
+ version: "1.0.0",
1088
+ supportedTransactionVersions: ["legacy", 0],
1089
+ signTransaction: this.#signTransaction
1090
+ }
1091
+ };
1092
+ }
1093
+ get connected() {
1094
+ return !!this.#authorization;
1095
+ }
1096
+ get isAuthorized() {
1097
+ return !!this.#authorization;
1098
+ }
1099
+ get currentAuthorization() {
1100
+ return this.#authorization;
1101
+ }
1102
+ get cachedAuthorizationResult() {
1103
+ return this.#authorizationCache.get();
1104
+ }
1105
+ #on = (event, listener) => {
1106
+ this.#listeners[event]?.push(listener) || (this.#listeners[event] = [listener]);
1107
+ return () => this.#off(event, listener);
1108
+ };
1109
+ #emit(event, ...args) {
1110
+ this.#listeners[event]?.forEach((listener) => listener.apply(null, args));
1111
+ }
1112
+ #off(event, listener) {
1113
+ this.#listeners[event] = this.#listeners[event]?.filter((existingListener) => listener !== existingListener);
1114
+ }
1115
+ #connect = async ({ silent } = {}) => {
1116
+ if (this.#connecting || this.connected) return { accounts: this.accounts };
1117
+ this.#connecting = true;
1118
+ try {
1119
+ if (silent) {
1120
+ const cachedAuthorization = await this.#authorizationCache.get();
1121
+ if (cachedAuthorization) {
1122
+ await this.#handleWalletCapabilitiesResult(cachedAuthorization.capabilities);
1123
+ await this.#handleAuthorizationResult(cachedAuthorization);
1124
+ } else return { accounts: this.accounts };
1125
+ } else await this.#performAuthorization();
1126
+ } catch (e) {
1127
+ throw new Error(getErrorMessage(e), { cause: e });
1128
+ } finally {
1129
+ this.#connecting = false;
1130
+ }
1131
+ return { accounts: this.accounts };
1132
+ };
1133
+ #performAuthorization = async (signInPayload) => {
1134
+ try {
1135
+ const cachedAuthorizationResult = await this.#authorizationCache.get();
1136
+ if (cachedAuthorizationResult) {
1137
+ this.#handleAuthorizationResult(cachedAuthorizationResult);
1138
+ return cachedAuthorizationResult;
1139
+ }
1140
+ const selectedChain = await this.#chainSelector.select(this.#chains);
1141
+ return await this.#transact(async (wallet) => {
1142
+ const [capabilities, mwaAuthorizationResult] = await Promise.all([wallet.getCapabilities(), wallet.authorize({
1143
+ chain: selectedChain,
1144
+ identity: this.#appIdentity,
1145
+ sign_in_payload: signInPayload
1146
+ })]);
1147
+ const accounts = this.#accountsToWalletStandardAccounts(mwaAuthorizationResult.accounts);
1148
+ const authorization = {
1149
+ ...mwaAuthorizationResult,
1150
+ accounts,
1151
+ chain: selectedChain,
1152
+ capabilities
1153
+ };
1154
+ Promise.all([
1155
+ this.#handleWalletCapabilitiesResult(capabilities),
1156
+ this.#authorizationCache.set(authorization),
1157
+ this.#handleAuthorizationResult(authorization)
1158
+ ]);
1159
+ return authorization;
1160
+ });
1161
+ } catch (e) {
1162
+ throw new Error(getErrorMessage(e), { cause: e });
1163
+ }
1164
+ };
1165
+ #handleAuthorizationResult = async (authorization) => {
1166
+ const didPublicKeysChange = this.#authorization == null || this.#authorization?.accounts.length !== authorization.accounts.length || this.#authorization.accounts.some((account, ii) => account.address !== authorization.accounts[ii].address);
1167
+ this.#authorization = authorization;
1168
+ if (didPublicKeysChange) this.#emit("change", { accounts: this.accounts });
1169
+ };
1170
+ #handleWalletCapabilitiesResult = async (capabilities) => {
1171
+ const supportsSignTransaction = capabilities.features.includes("solana:signTransactions");
1172
+ const supportsSignAndSendTransaction = capabilities.supports_sign_and_send_transactions;
1173
+ const didCapabilitiesChange = SolanaSignAndSendTransaction in this.features !== supportsSignAndSendTransaction || SolanaSignTransaction in this.features !== supportsSignTransaction;
1174
+ this.#optionalFeatures = {
1175
+ ...(supportsSignAndSendTransaction || !supportsSignAndSendTransaction && !supportsSignTransaction) && { [SolanaSignAndSendTransaction]: {
1176
+ version: "1.0.0",
1177
+ supportedTransactionVersions: ["legacy", 0],
1178
+ signAndSendTransaction: this.#signAndSendTransaction
1179
+ } },
1180
+ ...supportsSignTransaction && { [SolanaSignTransaction]: {
1181
+ version: "1.0.0",
1182
+ supportedTransactionVersions: ["legacy", 0],
1183
+ signTransaction: this.#signTransaction
1184
+ } }
1185
+ };
1186
+ if (didCapabilitiesChange) this.#emit("change", { features: this.features });
1187
+ };
1188
+ #performReauthorization = async (wallet, authToken, chain) => {
1189
+ try {
1190
+ const [capabilities, mwaAuthorizationResult] = await Promise.all([this.#authorization?.capabilities ?? await wallet.getCapabilities(), wallet.authorize({
1191
+ auth_token: authToken,
1192
+ identity: this.#appIdentity,
1193
+ chain
1194
+ })]);
1195
+ const accounts = this.#accountsToWalletStandardAccounts(mwaAuthorizationResult.accounts);
1196
+ const authorization = {
1197
+ ...mwaAuthorizationResult,
1198
+ accounts,
1199
+ chain,
1200
+ capabilities
1201
+ };
1202
+ Promise.all([this.#authorizationCache.set(authorization), this.#handleAuthorizationResult(authorization)]);
1203
+ } catch (e) {
1204
+ this.#disconnect();
1205
+ throw new Error(getErrorMessage(e), { cause: e });
1206
+ }
1207
+ };
1208
+ #disconnect = async () => {
1209
+ this.#authorizationCache.clear();
1210
+ this.#connecting = false;
1211
+ this.#connectionGeneration++;
1212
+ this.#authorization = void 0;
1213
+ this.#emit("change", { accounts: this.accounts });
1214
+ };
1215
+ #transact = async (callback) => {
1216
+ const walletUriBase = this.#authorization?.wallet_uri_base;
1217
+ const config = walletUriBase ? { baseUri: walletUriBase } : void 0;
1218
+ const currentConnectionGeneration = this.#connectionGeneration;
1219
+ const loadingSpinner = new EmbeddedLoadingSpinner();
1220
+ try {
1221
+ let associating = true;
1222
+ let timeout = void 0;
1223
+ const result = await Promise.race([checkLocalNetworkAccessPermission().then(async () => {
1224
+ loadingSpinner.init();
1225
+ const { wallet, close } = await startScenario(config);
1226
+ associating = false;
1227
+ loadingSpinner.addEventListener("close", (event) => {
1228
+ if (event) close();
1229
+ });
1230
+ loadingSpinner.open();
1231
+ const result = await callback(await wallet);
1232
+ loadingSpinner.close();
1233
+ close();
1234
+ return result;
1235
+ }), new Promise((_, reject) => {
1236
+ timeout = setTimeout(() => {
1237
+ if (associating) reject(new SolanaMobileWalletAdapterError(SolanaMobileWalletAdapterErrorCode.ERROR_ASSOCIATION_CANCELLED, "Wallet connection timed out", { event: void 0 }));
1238
+ }, WALLET_ASSOCIATION_TIMEOUT);
1239
+ })]);
1240
+ clearTimeout(timeout);
1241
+ return result;
1242
+ } catch (e) {
1243
+ loadingSpinner.close();
1244
+ if (this.#connectionGeneration !== currentConnectionGeneration) await new Promise(() => {});
1245
+ if (e instanceof Error && e.name === "SolanaMobileWalletAdapterError" && e.code === "ERROR_WALLET_NOT_FOUND") await this.#onWalletNotFound(this);
1246
+ throw e;
1247
+ }
1248
+ };
1249
+ #assertIsAuthorized = () => {
1250
+ if (!this.#authorization) throw new Error("Wallet not connected");
1251
+ return {
1252
+ authToken: this.#authorization.auth_token,
1253
+ chain: this.#authorization.chain
1254
+ };
1255
+ };
1256
+ #accountsToWalletStandardAccounts = (accounts) => {
1257
+ return accounts.map((account) => {
1258
+ const publicKey = toUint8Array(account.address);
1259
+ return {
1260
+ address: base58.encode(publicKey),
1261
+ publicKey,
1262
+ label: account.label,
1263
+ icon: account.icon,
1264
+ chains: account.chains ?? this.#chains,
1265
+ features: account.features ?? DEFAULT_FEATURES
1266
+ };
1267
+ });
1268
+ };
1269
+ #performSignTransactions = async (transactions) => {
1270
+ const { authToken, chain } = this.#assertIsAuthorized();
1271
+ try {
1272
+ const base64Transactions = transactions.map((tx) => {
1273
+ return fromUint8Array(tx);
1274
+ });
1275
+ return await this.#transact(async (wallet) => {
1276
+ await this.#performReauthorization(wallet, authToken, chain);
1277
+ return (await wallet.signTransactions({ payloads: base64Transactions })).signed_payloads.map(toUint8Array);
1278
+ });
1279
+ } catch (e) {
1280
+ throw new Error(getErrorMessage(e), { cause: e });
1281
+ }
1282
+ };
1283
+ #performSignAndSendTransaction = async (transaction, options) => {
1284
+ const { authToken, chain } = this.#assertIsAuthorized();
1285
+ try {
1286
+ return await this.#transact(async (wallet) => {
1287
+ const [capabilities] = await Promise.all([wallet.getCapabilities(), this.#performReauthorization(wallet, authToken, chain)]);
1288
+ if (capabilities.supports_sign_and_send_transactions) {
1289
+ const base64Transaction = fromUint8Array(transaction);
1290
+ return (await wallet.signAndSendTransactions({
1291
+ ...options,
1292
+ payloads: [base64Transaction]
1293
+ })).signatures.map(toUint8Array)[0];
1294
+ } else throw new Error("connected wallet does not support signAndSendTransaction");
1295
+ });
1296
+ } catch (e) {
1297
+ throw new Error(getErrorMessage(e), { cause: e });
1298
+ }
1299
+ };
1300
+ #signAndSendTransaction = async (...inputs) => {
1301
+ const outputs = [];
1302
+ for (const input of inputs) {
1303
+ const signature = await this.#performSignAndSendTransaction(input.transaction, input.options);
1304
+ outputs.push({ signature });
1305
+ }
1306
+ return outputs;
1307
+ };
1308
+ #signTransaction = async (...inputs) => {
1309
+ return (await this.#performSignTransactions(inputs.map(({ transaction }) => transaction))).map((signedTransaction) => {
1310
+ return { signedTransaction };
1311
+ });
1312
+ };
1313
+ #signMessage = async (...inputs) => {
1314
+ const { authToken, chain } = this.#assertIsAuthorized();
1315
+ const addresses = inputs.map(({ account }) => fromUint8Array(new Uint8Array(account.publicKey)));
1316
+ const messages = inputs.map(({ message }) => fromUint8Array(message));
1317
+ try {
1318
+ return await this.#transact(async (wallet) => {
1319
+ await this.#performReauthorization(wallet, authToken, chain);
1320
+ return (await wallet.signMessages({
1321
+ addresses,
1322
+ payloads: messages
1323
+ })).signed_payloads.map(toUint8Array).map((signedMessage) => {
1324
+ return {
1325
+ signedMessage,
1326
+ signature: signedMessage.slice(-SIGNATURE_LENGTH_IN_BYTES)
1327
+ };
1328
+ });
1329
+ });
1330
+ } catch (e) {
1331
+ throw new Error(getErrorMessage(e), { cause: e });
1332
+ }
1333
+ };
1334
+ #signIn = async (...inputs) => {
1335
+ const outputs = [];
1336
+ if (inputs.length > 1) for (const input of inputs) outputs.push(await this.#performSignIn(input));
1337
+ else return [await this.#performSignIn(inputs[0])];
1338
+ return outputs;
1339
+ };
1340
+ #performSignIn = async (input) => {
1341
+ this.#connecting = true;
1342
+ try {
1343
+ const authorizationResult = await this.#performAuthorization({
1344
+ ...input,
1345
+ domain: input?.domain ?? window.location.host
1346
+ });
1347
+ if (!authorizationResult.sign_in_result) throw new Error("Sign in failed, no sign in result returned by wallet");
1348
+ const signedInAddress = authorizationResult.sign_in_result.address;
1349
+ const signedInAccount = authorizationResult.accounts.find((acc) => acc.address == signedInAddress);
1350
+ return {
1351
+ account: {
1352
+ ...signedInAccount ?? { address: base58.encode(toUint8Array(signedInAddress)) },
1353
+ publicKey: toUint8Array(signedInAddress),
1354
+ chains: signedInAccount?.chains ?? this.#chains,
1355
+ features: signedInAccount?.features ?? authorizationResult.capabilities.features
1356
+ },
1357
+ signedMessage: toUint8Array(authorizationResult.sign_in_result.signed_message),
1358
+ signature: toUint8Array(authorizationResult.sign_in_result.signature)
1359
+ };
1360
+ } catch (e) {
1361
+ throw new Error(getErrorMessage(e), { cause: e });
1362
+ } finally {
1363
+ this.#connecting = false;
1364
+ }
1365
+ };
1366
+ };
1367
+ var RemoteSolanaMobileWalletAdapterWallet = class {
1368
+ #listeners = {};
1369
+ #version = "1.0.0";
1370
+ #name = SolanaMobileWalletAdapterRemoteWalletName;
1371
+ #url = "https://solanamobile.com/wallets";
1372
+ #icon = icon;
1373
+ #appIdentity;
1374
+ #authorization;
1375
+ #authorizationCache;
1376
+ #connecting = false;
1377
+ /**
1378
+ * Every time the connection is recycled in some way (eg. `disconnect()` is called)
1379
+ * increment this and use it to make sure that `transact` calls from the previous
1380
+ * 'generation' don't continue to do work and throw exceptions.
1381
+ */
1382
+ #connectionGeneration = 0;
1383
+ #chains = [];
1384
+ #chainSelector;
1385
+ #optionalFeatures;
1386
+ #onWalletNotFound;
1387
+ #hostAuthority;
1388
+ #session;
1389
+ get version() {
1390
+ return this.#version;
1391
+ }
1392
+ get name() {
1393
+ return this.#name;
1394
+ }
1395
+ get url() {
1396
+ return this.#url;
1397
+ }
1398
+ get icon() {
1399
+ return this.#icon;
1400
+ }
1401
+ get chains() {
1402
+ return this.#chains;
1403
+ }
1404
+ get features() {
1405
+ return {
1406
+ [StandardConnect]: {
1407
+ version: "1.0.0",
1408
+ connect: this.#connect
1409
+ },
1410
+ [StandardDisconnect]: {
1411
+ version: "1.0.0",
1412
+ disconnect: this.#disconnect
1413
+ },
1414
+ [StandardEvents]: {
1415
+ version: "1.0.0",
1416
+ on: this.#on
1417
+ },
1418
+ [SolanaSignMessage]: {
1419
+ version: "1.0.0",
1420
+ signMessage: this.#signMessage
1421
+ },
1422
+ [SolanaSignIn]: {
1423
+ version: "1.0.0",
1424
+ signIn: this.#signIn
1425
+ },
1426
+ ...this.#optionalFeatures
1427
+ };
1428
+ }
1429
+ get accounts() {
1430
+ return this.#authorization?.accounts ?? [];
1431
+ }
1432
+ constructor(config) {
1433
+ this.#authorizationCache = config.authorizationCache;
1434
+ this.#appIdentity = config.appIdentity;
1435
+ this.#chains = config.chains;
1436
+ this.#chainSelector = config.chainSelector;
1437
+ this.#hostAuthority = config.remoteHostAuthority;
1438
+ this.#onWalletNotFound = config.onWalletNotFound;
1439
+ this.#optionalFeatures = {
1440
+ [SolanaSignAndSendTransaction]: {
1441
+ version: "1.0.0",
1442
+ supportedTransactionVersions: ["legacy", 0],
1443
+ signAndSendTransaction: this.#signAndSendTransaction
1444
+ },
1445
+ [SolanaSignTransaction]: {
1446
+ version: "1.0.0",
1447
+ supportedTransactionVersions: ["legacy", 0],
1448
+ signTransaction: this.#signTransaction
1449
+ }
1450
+ };
1451
+ }
1452
+ get connected() {
1453
+ return !!this.#session && !!this.#authorization;
1454
+ }
1455
+ get isAuthorized() {
1456
+ return !!this.#authorization;
1457
+ }
1458
+ get currentAuthorization() {
1459
+ return this.#authorization;
1460
+ }
1461
+ get cachedAuthorizationResult() {
1462
+ return this.#authorizationCache.get();
1463
+ }
1464
+ #on = (event, listener) => {
1465
+ this.#listeners[event]?.push(listener) || (this.#listeners[event] = [listener]);
1466
+ return () => this.#off(event, listener);
1467
+ };
1468
+ #emit(event, ...args) {
1469
+ this.#listeners[event]?.forEach((listener) => listener.apply(null, args));
1470
+ }
1471
+ #off(event, listener) {
1472
+ this.#listeners[event] = this.#listeners[event]?.filter((existingListener) => listener !== existingListener);
1473
+ }
1474
+ #connect = async (_input = {}) => {
1475
+ if (this.#connecting || this.connected) return { accounts: this.accounts };
1476
+ this.#connecting = true;
1477
+ try {
1478
+ await this.#performAuthorization();
1479
+ } catch (e) {
1480
+ throw new Error(getErrorMessage(e), { cause: e });
1481
+ } finally {
1482
+ this.#connecting = false;
1483
+ }
1484
+ return { accounts: this.accounts };
1485
+ };
1486
+ #performAuthorization = async (signInPayload) => {
1487
+ try {
1488
+ const cachedAuthorizationResult = await this.#authorizationCache.get();
1489
+ if (cachedAuthorizationResult) {
1490
+ this.#handleAuthorizationResult(cachedAuthorizationResult);
1491
+ return cachedAuthorizationResult;
1492
+ }
1493
+ if (this.#session) this.#session = void 0;
1494
+ const selectedChain = await this.#chainSelector.select(this.#chains);
1495
+ return await this.#transact(async (wallet) => {
1496
+ const [capabilities, mwaAuthorizationResult] = await Promise.all([wallet.getCapabilities(), wallet.authorize({
1497
+ chain: selectedChain,
1498
+ identity: this.#appIdentity,
1499
+ sign_in_payload: signInPayload
1500
+ })]);
1501
+ const accounts = this.#accountsToWalletStandardAccounts(mwaAuthorizationResult.accounts);
1502
+ const authorizationResult = {
1503
+ ...mwaAuthorizationResult,
1504
+ accounts,
1505
+ chain: selectedChain,
1506
+ capabilities
1507
+ };
1508
+ Promise.all([
1509
+ this.#handleWalletCapabilitiesResult(capabilities),
1510
+ this.#authorizationCache.set(authorizationResult),
1511
+ this.#handleAuthorizationResult(authorizationResult)
1512
+ ]);
1513
+ return authorizationResult;
1514
+ });
1515
+ } catch (e) {
1516
+ throw new Error(getErrorMessage(e), { cause: e });
1517
+ }
1518
+ };
1519
+ #handleAuthorizationResult = async (authorization) => {
1520
+ const didPublicKeysChange = this.#authorization == null || this.#authorization?.accounts.length !== authorization.accounts.length || this.#authorization.accounts.some((account, ii) => account.address !== authorization.accounts[ii].address);
1521
+ this.#authorization = authorization;
1522
+ if (didPublicKeysChange) this.#emit("change", { accounts: this.accounts });
1523
+ };
1524
+ #handleWalletCapabilitiesResult = async (capabilities) => {
1525
+ const supportsSignTransaction = capabilities.features.includes("solana:signTransactions");
1526
+ const supportsSignAndSendTransaction = capabilities.supports_sign_and_send_transactions || capabilities.features.includes("solana:signAndSendTransaction");
1527
+ const didCapabilitiesChange = SolanaSignAndSendTransaction in this.features !== supportsSignAndSendTransaction || SolanaSignTransaction in this.features !== supportsSignTransaction;
1528
+ this.#optionalFeatures = {
1529
+ ...supportsSignAndSendTransaction && { [SolanaSignAndSendTransaction]: {
1530
+ version: "1.0.0",
1531
+ supportedTransactionVersions: capabilities.supported_transaction_versions,
1532
+ signAndSendTransaction: this.#signAndSendTransaction
1533
+ } },
1534
+ ...supportsSignTransaction && { [SolanaSignTransaction]: {
1535
+ version: "1.0.0",
1536
+ supportedTransactionVersions: capabilities.supported_transaction_versions,
1537
+ signTransaction: this.#signTransaction
1538
+ } }
1539
+ };
1540
+ if (didCapabilitiesChange) this.#emit("change", { features: this.features });
1541
+ };
1542
+ #performReauthorization = async (wallet, authToken, chain) => {
1543
+ try {
1544
+ const [capabilities, mwaAuthorizationResult] = await Promise.all([this.#authorization?.capabilities ?? await wallet.getCapabilities(), wallet.authorize({
1545
+ auth_token: authToken,
1546
+ identity: this.#appIdentity,
1547
+ chain
1548
+ })]);
1549
+ const accounts = this.#accountsToWalletStandardAccounts(mwaAuthorizationResult.accounts);
1550
+ const authorization = {
1551
+ ...mwaAuthorizationResult,
1552
+ accounts,
1553
+ chain,
1554
+ capabilities
1555
+ };
1556
+ Promise.all([this.#authorizationCache.set(authorization), this.#handleAuthorizationResult(authorization)]);
1557
+ } catch (e) {
1558
+ this.#disconnect();
1559
+ throw new Error(getErrorMessage(e), { cause: e });
1560
+ }
1561
+ };
1562
+ #disconnect = async () => {
1563
+ this.#session?.close();
1564
+ this.#authorizationCache.clear();
1565
+ this.#connecting = false;
1566
+ this.#connectionGeneration++;
1567
+ this.#authorization = void 0;
1568
+ this.#session = void 0;
1569
+ this.#emit("change", { accounts: this.accounts });
1570
+ };
1571
+ #transact = async (callback) => {
1572
+ const walletUriBase = this.#authorization?.wallet_uri_base;
1573
+ const remoteConfig = {
1574
+ ...walletUriBase ? { baseUri: walletUriBase } : void 0,
1575
+ remoteHostAuthority: this.#hostAuthority
1576
+ };
1577
+ const currentConnectionGeneration = this.#connectionGeneration;
1578
+ const modal = new RemoteConnectionModal();
1579
+ if (this.#session) return callback(this.#session.wallet);
1580
+ try {
1581
+ modal.init();
1582
+ modal.open();
1583
+ const { associationUrl, close, wallet } = await startRemoteScenario(remoteConfig);
1584
+ const removeCloseListener = modal.addEventListener("close", (event) => {
1585
+ if (event) close();
1586
+ });
1587
+ modal.populateQRCode(associationUrl.toString());
1588
+ this.#session = {
1589
+ close,
1590
+ wallet: await wallet
1591
+ };
1592
+ removeCloseListener();
1593
+ modal.close();
1594
+ return await callback(this.#session.wallet);
1595
+ } catch (e) {
1596
+ modal.close();
1597
+ if (this.#connectionGeneration !== currentConnectionGeneration) await new Promise(() => {});
1598
+ if (e instanceof Error && e.name === "SolanaMobileWalletAdapterError" && e.code === "ERROR_WALLET_NOT_FOUND") await this.#onWalletNotFound(this);
1599
+ throw e;
1600
+ }
1601
+ };
1602
+ #assertIsAuthorized = () => {
1603
+ if (!this.#authorization) throw new Error("Wallet not connected");
1604
+ return {
1605
+ authToken: this.#authorization.auth_token,
1606
+ chain: this.#authorization.chain
1607
+ };
1608
+ };
1609
+ #accountsToWalletStandardAccounts = (accounts) => {
1610
+ return accounts.map((account) => {
1611
+ const publicKey = toUint8Array(account.address);
1612
+ return {
1613
+ address: base58.encode(publicKey),
1614
+ publicKey,
1615
+ label: account.label,
1616
+ icon: account.icon,
1617
+ chains: account.chains ?? this.#chains,
1618
+ features: account.features ?? DEFAULT_FEATURES
1619
+ };
1620
+ });
1621
+ };
1622
+ #performSignTransactions = async (transactions) => {
1623
+ const { authToken, chain } = this.#assertIsAuthorized();
1624
+ try {
1625
+ return await this.#transact(async (wallet) => {
1626
+ await this.#performReauthorization(wallet, authToken, chain);
1627
+ return (await wallet.signTransactions({ payloads: transactions.map(fromUint8Array) })).signed_payloads.map(toUint8Array);
1628
+ });
1629
+ } catch (e) {
1630
+ throw new Error(getErrorMessage(e), { cause: e });
1631
+ }
1632
+ };
1633
+ #performSignAndSendTransaction = async (transaction, options) => {
1634
+ const { authToken, chain } = this.#assertIsAuthorized();
1635
+ try {
1636
+ return await this.#transact(async (wallet) => {
1637
+ const [capabilities] = await Promise.all([wallet.getCapabilities(), this.#performReauthorization(wallet, authToken, chain)]);
1638
+ if (capabilities.supports_sign_and_send_transactions) return (await wallet.signAndSendTransactions({
1639
+ ...options,
1640
+ payloads: [fromUint8Array(transaction)]
1641
+ })).signatures.map(toUint8Array)[0];
1642
+ else throw new Error("connected wallet does not support signAndSendTransaction");
1643
+ });
1644
+ } catch (e) {
1645
+ throw new Error(getErrorMessage(e), { cause: e });
1646
+ }
1647
+ };
1648
+ #signAndSendTransaction = async (...inputs) => {
1649
+ const outputs = [];
1650
+ for (const input of inputs) {
1651
+ const signature = await this.#performSignAndSendTransaction(input.transaction, input.options);
1652
+ outputs.push({ signature });
1653
+ }
1654
+ return outputs;
1655
+ };
1656
+ #signTransaction = async (...inputs) => {
1657
+ return (await this.#performSignTransactions(inputs.map(({ transaction }) => transaction))).map((signedTransaction) => {
1658
+ return { signedTransaction };
1659
+ });
1660
+ };
1661
+ #signMessage = async (...inputs) => {
1662
+ const { authToken, chain } = this.#assertIsAuthorized();
1663
+ const addresses = inputs.map(({ account }) => fromUint8Array(new Uint8Array(account.publicKey)));
1664
+ const messages = inputs.map(({ message }) => fromUint8Array(message));
1665
+ try {
1666
+ return await this.#transact(async (wallet) => {
1667
+ await this.#performReauthorization(wallet, authToken, chain);
1668
+ return (await wallet.signMessages({
1669
+ addresses,
1670
+ payloads: messages
1671
+ })).signed_payloads.map(toUint8Array).map((signedMessage) => {
1672
+ return {
1673
+ signedMessage,
1674
+ signature: signedMessage.slice(-SIGNATURE_LENGTH_IN_BYTES)
1675
+ };
1676
+ });
1677
+ });
1678
+ } catch (e) {
1679
+ throw new Error(getErrorMessage(e), { cause: e });
1680
+ }
1681
+ };
1682
+ #signIn = async (...inputs) => {
1683
+ const outputs = [];
1684
+ if (inputs.length > 1) for (const input of inputs) outputs.push(await this.#performSignIn(input));
1685
+ else return [await this.#performSignIn(inputs[0])];
1686
+ return outputs;
1687
+ };
1688
+ #performSignIn = async (input) => {
1689
+ this.#connecting = true;
1690
+ try {
1691
+ const authorizationResult = await this.#performAuthorization({
1692
+ ...input,
1693
+ domain: input?.domain ?? window.location.host
1694
+ });
1695
+ if (!authorizationResult.sign_in_result) throw new Error("Sign in failed, no sign in result returned by wallet");
1696
+ const signedInAddress = authorizationResult.sign_in_result.address;
1697
+ const signedInAccount = authorizationResult.accounts.find((acc) => acc.address == signedInAddress);
1698
+ return {
1699
+ account: {
1700
+ ...signedInAccount ?? { address: base58.encode(toUint8Array(signedInAddress)) },
1701
+ publicKey: toUint8Array(signedInAddress),
1702
+ chains: signedInAccount?.chains ?? this.#chains,
1703
+ features: signedInAccount?.features ?? authorizationResult.capabilities.features
1704
+ },
1705
+ signedMessage: toUint8Array(authorizationResult.sign_in_result.signed_message),
1706
+ signature: toUint8Array(authorizationResult.sign_in_result.signature)
1707
+ };
1708
+ } catch (e) {
1709
+ throw new Error(getErrorMessage(e), { cause: e });
1710
+ } finally {
1711
+ this.#connecting = false;
1712
+ }
1713
+ };
1714
+ };
1715
+ //#endregion
1716
+ //#region src/initialize.ts
1926
1717
  function registerMwa(config) {
1927
- if (typeof window === 'undefined') {
1928
- console.warn(`MWA not registered: no window object`);
1929
- return;
1930
- }
1931
- if (!window.isSecureContext) {
1932
- console.warn(`MWA not registered: secure context required (https)`);
1933
- return;
1934
- }
1935
- // Local association technically is possible in a webview, but we prevent registration
1936
- // by default because it usually fails in the most common cases (e.g wallet browsers).
1937
- if (getIsLocalAssociationSupported() && !isWebView(navigator.userAgent)) {
1938
- registerWallet(new LocalSolanaMobileWalletAdapterWallet(config));
1939
- }
1940
- else if (getIsRemoteAssociationSupported() && config.remoteHostAuthority !== undefined) {
1941
- registerWallet(new RemoteSolanaMobileWalletAdapterWallet({ ...config, remoteHostAuthority: config.remoteHostAuthority }));
1942
- }
1943
- else ;
1944
- }
1945
-
1946
- const WALLET_NOT_FOUND_ERROR_MESSAGE = 'To use mobile wallet adapter, you must have a compatible mobile wallet application installed on your device.';
1947
- const BROWSER_NOT_SUPPORTED_ERROR_MESSAGE = 'This browser appears to be incompatible with mobile wallet adapter. Open this page in a compatible mobile browser app and try again.';
1948
- class ErrorModal extends EmbeddedModal {
1949
- contentStyles = css;
1950
- contentHtml = ErrorDialogHtml;
1951
- initWithError(error) {
1952
- super.init();
1953
- this.populateError(error);
1954
- }
1955
- populateError(error) {
1956
- const errorMessageElement = this.dom?.getElementById('mobile-wallet-adapter-error-message');
1957
- const actionBtn = this.dom?.getElementById('mobile-wallet-adapter-error-action');
1958
- if (errorMessageElement) {
1959
- if (error.name === 'SolanaMobileWalletAdapterError') {
1960
- switch (error.code) {
1961
- case 'ERROR_WALLET_NOT_FOUND':
1962
- errorMessageElement.innerHTML = WALLET_NOT_FOUND_ERROR_MESSAGE;
1963
- if (actionBtn)
1964
- actionBtn.addEventListener('click', () => {
1965
- window.location.href = 'https://solanamobile.com/wallets';
1966
- });
1967
- return;
1968
- case 'ERROR_BROWSER_NOT_SUPPORTED':
1969
- errorMessageElement.innerHTML = BROWSER_NOT_SUPPORTED_ERROR_MESSAGE;
1970
- if (actionBtn)
1971
- actionBtn.style.display = 'none';
1972
- return;
1973
- }
1974
- }
1975
- errorMessageElement.innerHTML = `An unexpected error occurred: ${error.message}`;
1976
- }
1977
- else {
1978
- console.log('Failed to locate error dialog element');
1979
- }
1980
- }
1981
- }
1718
+ if (typeof window === "undefined") {
1719
+ console.warn(`MWA not registered: no window object`);
1720
+ return;
1721
+ }
1722
+ if (!window.isSecureContext) {
1723
+ console.warn(`MWA not registered: secure context required (https)`);
1724
+ return;
1725
+ }
1726
+ const userAgent = navigator.userAgent;
1727
+ if (getIsLocalAssociationSupported() && (!isWebView(userAgent) || isSolanaMobileWebShell(userAgent))) registerWallet(new LocalSolanaMobileWalletAdapterWallet(config));
1728
+ else if (getIsRemoteAssociationSupported() && config.remoteHostAuthority !== void 0) registerWallet(new RemoteSolanaMobileWalletAdapterWallet({
1729
+ ...config,
1730
+ remoteHostAuthority: config.remoteHostAuthority
1731
+ }));
1732
+ }
1733
+ //#endregion
1734
+ //#region src/embedded-modal/errorModal.ts
1735
+ const WALLET_NOT_FOUND_ERROR_MESSAGE = "To use mobile wallet adapter, you must have a compatible mobile wallet application installed on your device.";
1736
+ const BROWSER_NOT_SUPPORTED_ERROR_MESSAGE = "This browser appears to be incompatible with mobile wallet adapter. Open this page in a compatible mobile browser app and try again.";
1737
+ var ErrorModal = class extends EmbeddedModal {
1738
+ contentStyles = css;
1739
+ contentHtml = ErrorDialogHtml;
1740
+ initWithError(error) {
1741
+ super.init();
1742
+ this.populateError(error);
1743
+ }
1744
+ populateError(error) {
1745
+ const errorMessageElement = this.dom?.getElementById("mobile-wallet-adapter-error-message");
1746
+ const actionBtn = this.dom?.getElementById("mobile-wallet-adapter-error-action");
1747
+ if (errorMessageElement) {
1748
+ if (error.name === "SolanaMobileWalletAdapterError") switch (error.code) {
1749
+ case "ERROR_WALLET_NOT_FOUND":
1750
+ errorMessageElement.innerHTML = WALLET_NOT_FOUND_ERROR_MESSAGE;
1751
+ if (actionBtn) actionBtn.addEventListener("click", () => {
1752
+ window.location.href = "https://solanamobile.com/wallets";
1753
+ });
1754
+ return;
1755
+ case "ERROR_BROWSER_NOT_SUPPORTED":
1756
+ errorMessageElement.innerHTML = BROWSER_NOT_SUPPORTED_ERROR_MESSAGE;
1757
+ if (actionBtn) actionBtn.style.display = "none";
1758
+ return;
1759
+ }
1760
+ errorMessageElement.innerHTML = `An unexpected error occurred: ${error.message}`;
1761
+ } else console.log("Failed to locate error dialog element");
1762
+ }
1763
+ };
1982
1764
  const ErrorDialogHtml = `
1983
1765
  <svg class="mobile-wallet-adapter-embedded-modal-error-icon" xmlns="http://www.w3.org/2000/svg" height="50px" viewBox="0 -960 960 960" width="50px" fill="#000000"><path d="M 280,-80 Q 197,-80 138.5,-138.5 80,-197 80,-280 80,-363 138.5,-421.5 197,-480 280,-480 q 83,0 141.5,58.5 58.5,58.5 58.5,141.5 0,83 -58.5,141.5 Q 363,-80 280,-80 Z M 824,-120 568,-376 Q 556,-389 542.5,-402.5 529,-416 516,-428 q 38,-24 61,-64 23,-40 23,-88 0,-75 -52.5,-127.5 Q 495,-760 420,-760 345,-760 292.5,-707.5 240,-655 240,-580 q 0,6 0.5,11.5 0.5,5.5 1.5,11.5 -18,2 -39.5,8 -21.5,6 -38.5,14 -2,-11 -3,-22 -1,-11 -1,-23 0,-109 75.5,-184.5 Q 311,-840 420,-840 q 109,0 184.5,75.5 75.5,75.5 75.5,184.5 0,43 -13.5,81.5 Q 653,-460 629,-428 l 251,252 z m -615,-61 71,-71 70,71 29,-28 -71,-71 71,-71 -28,-28 -71,71 -71,-71 -28,28 71,71 -71,71 z"/></svg>
1984
1766
  <div class="mobile-wallet-adapter-embedded-modal-title">We can't find a wallet.</div>
@@ -2038,115 +1820,81 @@ const css = `
2038
1820
  }
2039
1821
  }
2040
1822
  `;
2041
-
1823
+ //#endregion
1824
+ //#region src/createDefaultWalletNotFoundHandler.ts
2042
1825
  async function defaultErrorModalWalletNotFoundHandler() {
2043
- if (typeof window !== 'undefined') {
2044
- const userAgent = window.navigator.userAgent.toLowerCase();
2045
- const errorDialog = new ErrorModal();
2046
- if (userAgent.includes('wv')) { // Android WebView
2047
- // MWA is not supported in this browser so we inform the user
2048
- // errorDialog.initWithError(
2049
- // new SolanaMobileWalletAdapterError(
2050
- // SolanaMobileWalletAdapterErrorCode.ERROR_BROWSER_NOT_SUPPORTED,
2051
- // ''
2052
- // )
2053
- // );
2054
- // TODO: investigate why instantiating a new SolanaMobileWalletAdapterError here breaks treeshaking
2055
- errorDialog.initWithError({
2056
- name: 'SolanaMobileWalletAdapterError',
2057
- code: 'ERROR_BROWSER_NOT_SUPPORTED',
2058
- message: ''
2059
- });
2060
- }
2061
- else { // Browser, user does not have a wallet installed.
2062
- // errorDialog.initWithError(
2063
- // new SolanaMobileWalletAdapterError(
2064
- // SolanaMobileWalletAdapterErrorCode.ERROR_WALLET_NOT_FOUND,
2065
- // ''
2066
- // )
2067
- // );
2068
- // TODO: investigate why instantiating a new SolanaMobileWalletAdapterError here breaks treeshaking
2069
- errorDialog.initWithError({
2070
- name: 'SolanaMobileWalletAdapterError',
2071
- code: 'ERROR_WALLET_NOT_FOUND',
2072
- message: ''
2073
- });
2074
- }
2075
- errorDialog.open();
2076
- }
1826
+ if (typeof window !== "undefined") {
1827
+ const userAgent = window.navigator.userAgent.toLowerCase();
1828
+ const errorDialog = new ErrorModal();
1829
+ if (userAgent.includes("wv")) errorDialog.initWithError({
1830
+ name: "SolanaMobileWalletAdapterError",
1831
+ code: "ERROR_BROWSER_NOT_SUPPORTED",
1832
+ message: ""
1833
+ });
1834
+ else errorDialog.initWithError({
1835
+ name: "SolanaMobileWalletAdapterError",
1836
+ code: "ERROR_WALLET_NOT_FOUND",
1837
+ message: ""
1838
+ });
1839
+ errorDialog.open();
1840
+ }
2077
1841
  }
2078
1842
  function createDefaultWalletNotFoundHandler() {
2079
- return async () => { defaultErrorModalWalletNotFoundHandler(); };
1843
+ return async () => {
1844
+ defaultErrorModalWalletNotFoundHandler();
1845
+ };
2080
1846
  }
2081
-
2082
- const CACHE_KEY = 'SolanaMobileWalletAdapterDefaultAuthorizationCache';
1847
+ //#endregion
1848
+ //#region src/createDefaultAuthorizationCache.ts
1849
+ const CACHE_KEY = "SolanaMobileWalletAdapterDefaultAuthorizationCache";
2083
1850
  function createDefaultAuthorizationCache() {
2084
- let storage;
2085
- try {
2086
- storage = window.localStorage;
2087
- // eslint-disable-next-line no-empty
2088
- }
2089
- catch { }
2090
- return {
2091
- async clear() {
2092
- if (!storage) {
2093
- return;
2094
- }
2095
- try {
2096
- storage.removeItem(CACHE_KEY);
2097
- // eslint-disable-next-line no-empty
2098
- }
2099
- catch { }
2100
- },
2101
- async get() {
2102
- if (!storage) {
2103
- return;
2104
- }
2105
- try {
2106
- const parsed = JSON.parse(storage.getItem(CACHE_KEY));
2107
- if (parsed && parsed.accounts) {
2108
- const parsedAccounts = parsed.accounts.map((account) => {
2109
- return {
2110
- ...account,
2111
- publicKey: 'publicKey' in account
2112
- ? new Uint8Array(Object.values(account.publicKey)) // Rebuild publicKey for WalletAccount
2113
- : base58.decode(account.address), // Fallback, get publicKey from address
2114
- };
2115
- });
2116
- return { ...parsed, accounts: parsedAccounts };
2117
- }
2118
- else
2119
- return parsed || undefined;
2120
- // eslint-disable-next-line no-empty
2121
- }
2122
- catch { }
2123
- },
2124
- async set(authorization) {
2125
- if (!storage) {
2126
- return;
2127
- }
2128
- try {
2129
- storage.setItem(CACHE_KEY, JSON.stringify(authorization));
2130
- // eslint-disable-next-line no-empty
2131
- }
2132
- catch { }
2133
- },
2134
- };
2135
- }
2136
-
1851
+ let storage;
1852
+ try {
1853
+ storage = window.localStorage;
1854
+ } catch {}
1855
+ return {
1856
+ async clear() {
1857
+ if (!storage) return;
1858
+ try {
1859
+ storage.removeItem(CACHE_KEY);
1860
+ } catch {}
1861
+ },
1862
+ async get() {
1863
+ if (!storage) return;
1864
+ try {
1865
+ const parsed = JSON.parse(storage.getItem(CACHE_KEY));
1866
+ if (parsed && parsed.accounts) {
1867
+ const parsedAccounts = parsed.accounts.map((account) => {
1868
+ return {
1869
+ ...account,
1870
+ publicKey: "publicKey" in account ? new Uint8Array(Object.values(account.publicKey)) : base58.decode(account.address)
1871
+ };
1872
+ });
1873
+ return {
1874
+ ...parsed,
1875
+ accounts: parsedAccounts
1876
+ };
1877
+ } else return parsed || void 0;
1878
+ } catch {}
1879
+ },
1880
+ async set(authorization) {
1881
+ if (!storage) return;
1882
+ try {
1883
+ storage.setItem(CACHE_KEY, JSON.stringify(authorization));
1884
+ } catch {}
1885
+ }
1886
+ };
1887
+ }
1888
+ //#endregion
1889
+ //#region src/createDefaultChainSelector.ts
2137
1890
  function createDefaultChainSelector() {
2138
- return {
2139
- async select(chains) {
2140
- if (chains.length === 1) {
2141
- return chains[0];
2142
- }
2143
- else if (chains.includes(SOLANA_MAINNET_CHAIN)) {
2144
- return SOLANA_MAINNET_CHAIN;
2145
- }
2146
- else
2147
- return chains[0];
2148
- },
2149
- };
1891
+ return { async select(chains) {
1892
+ if (chains.length === 1) return chains[0];
1893
+ else if (chains.includes(SOLANA_MAINNET_CHAIN)) return SOLANA_MAINNET_CHAIN;
1894
+ else return chains[0];
1895
+ } };
2150
1896
  }
2151
-
1897
+ //#endregion
2152
1898
  export { LocalSolanaMobileWalletAdapterWallet, RemoteSolanaMobileWalletAdapterWallet, SolanaMobileWalletAdapterRemoteWalletName, SolanaMobileWalletAdapterWalletName, createDefaultAuthorizationCache, createDefaultChainSelector, createDefaultWalletNotFoundHandler, defaultErrorModalWalletNotFoundHandler, registerMwa };
1899
+
1900
+ //# sourceMappingURL=index.browser.js.map