@pooflabs/web 0.0.83 → 0.0.85-rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dist/auth/providers/solana-mobile-wallet-provider.d.ts +42 -20
  2. package/dist/{index-CpaP1yGp.esm.js → index-4TKz3Mn_.esm.js} +14 -364
  3. package/dist/index-4TKz3Mn_.esm.js.map +1 -0
  4. package/dist/{index-FviRSm3S.js → index-BoWQVxMI.js} +13 -363
  5. package/dist/index-BoWQVxMI.js.map +1 -0
  6. package/dist/{index-CP_wLmYu.esm.js → index-Buu8v-Oe.esm.js} +13 -363
  7. package/dist/index-Buu8v-Oe.esm.js.map +1 -0
  8. package/dist/{index-BxXQhFLQ.js → index-C2p1Cldz.js} +14 -364
  9. package/dist/index-C2p1Cldz.js.map +1 -0
  10. package/dist/index-COF6zNCs.esm.js +6 -0
  11. package/dist/index-COF6zNCs.esm.js.map +1 -0
  12. package/dist/{index-DQWyH96R.js → index-DlyeWzMk.js} +2 -2
  13. package/dist/index-DlyeWzMk.js.map +1 -0
  14. package/dist/{index-DP0xF34Z.js → index-R7cvXys2.js} +486 -299
  15. package/dist/index-R7cvXys2.js.map +1 -0
  16. package/dist/{index-B7yaLhND.esm.js → index-ZKzq5QT_.esm.js} +487 -300
  17. package/dist/index-ZKzq5QT_.esm.js.map +1 -0
  18. package/dist/{index.browser-_zN3Uapq.js → index.browser-BQSN-6X2.js} +846 -815
  19. package/dist/index.browser-BQSN-6X2.js.map +1 -0
  20. package/dist/index.browser-DZCNegue.js +6070 -0
  21. package/dist/index.browser-DZCNegue.js.map +1 -0
  22. package/dist/{index.browser-B_wQp2A8.esm.js → index.browser-tPepE5fo.esm.js} +836 -802
  23. package/dist/index.browser-tPepE5fo.esm.js.map +1 -0
  24. package/dist/index.browser-zfGYm0ST.esm.js +6060 -0
  25. package/dist/index.browser-zfGYm0ST.esm.js.map +1 -0
  26. package/dist/index.esm.js +1 -1
  27. package/dist/index.js +1 -1
  28. package/dist/{index.native-Dkf8NZ2O.js → index.native-BGaUOX9f.js} +61 -42
  29. package/dist/index.native-BGaUOX9f.js.map +1 -0
  30. package/dist/{index.native-CyEwEeKr.esm.js → index.native-BPQqeP6_.esm.js} +62 -43
  31. package/dist/index.native-BPQqeP6_.esm.js.map +1 -0
  32. package/dist/index.native.esm.js +1 -1
  33. package/dist/index.native.js +1 -1
  34. package/dist/{phantom-wallet-provider-fkcFbwPk.esm.js → phantom-wallet-provider-DheGhOp4.esm.js} +37 -6
  35. package/dist/phantom-wallet-provider-DheGhOp4.esm.js.map +1 -0
  36. package/dist/{phantom-wallet-provider-CVyVJmH0.js → phantom-wallet-provider-MFcwrRpJ.js} +37 -6
  37. package/dist/phantom-wallet-provider-MFcwrRpJ.js.map +1 -0
  38. package/dist/{privy-wallet-provider-CrBZ52nR.js → privy-wallet-provider-CFw6o7O5.js} +3 -3
  39. package/dist/privy-wallet-provider-CFw6o7O5.js.map +1 -0
  40. package/dist/{privy-wallet-provider-CpHAxPcv.esm.js → privy-wallet-provider-D15Au3j8.esm.js} +3 -3
  41. package/dist/privy-wallet-provider-D15Au3j8.esm.js.map +1 -0
  42. package/dist/solana-mobile-wallet-provider-BM9wdF0m.js +719 -0
  43. package/dist/solana-mobile-wallet-provider-BM9wdF0m.js.map +1 -0
  44. package/dist/solana-mobile-wallet-provider-ChHh-U_p.esm.js +698 -0
  45. package/dist/solana-mobile-wallet-provider-ChHh-U_p.esm.js.map +1 -0
  46. package/package.json +3 -2
  47. package/dist/index-B7yaLhND.esm.js.map +0 -1
  48. package/dist/index-BxXQhFLQ.js.map +0 -1
  49. package/dist/index-CP_wLmYu.esm.js.map +0 -1
  50. package/dist/index-CpaP1yGp.esm.js.map +0 -1
  51. package/dist/index-DP0xF34Z.js.map +0 -1
  52. package/dist/index-DQWyH96R.js.map +0 -1
  53. package/dist/index-DfOd8wW4.esm.js +0 -6
  54. package/dist/index-DfOd8wW4.esm.js.map +0 -1
  55. package/dist/index-FviRSm3S.js.map +0 -1
  56. package/dist/index.browser-1_M66nQ6.esm.js +0 -1096
  57. package/dist/index.browser-1_M66nQ6.esm.js.map +0 -1
  58. package/dist/index.browser-B_wQp2A8.esm.js.map +0 -1
  59. package/dist/index.browser-BfnOoa_h.js +0 -1099
  60. package/dist/index.browser-BfnOoa_h.js.map +0 -1
  61. package/dist/index.browser-BhppfDyf.js +0 -105
  62. package/dist/index.browser-BhppfDyf.js.map +0 -1
  63. package/dist/index.browser-OvGNsMPu.esm.js +0 -1002
  64. package/dist/index.browser-OvGNsMPu.esm.js.map +0 -1
  65. package/dist/index.browser-_zN3Uapq.js.map +0 -1
  66. package/dist/index.browser-vUinl_9y.esm.js +0 -102
  67. package/dist/index.browser-vUinl_9y.esm.js.map +0 -1
  68. package/dist/index.browser-vuTr40so.js +0 -1008
  69. package/dist/index.browser-vuTr40so.js.map +0 -1
  70. package/dist/index.native-CyEwEeKr.esm.js.map +0 -1
  71. package/dist/index.native-Dkf8NZ2O.js.map +0 -1
  72. package/dist/phantom-wallet-provider-CVyVJmH0.js.map +0 -1
  73. package/dist/phantom-wallet-provider-fkcFbwPk.esm.js.map +0 -1
  74. package/dist/privy-wallet-provider-CpHAxPcv.esm.js.map +0 -1
  75. package/dist/privy-wallet-provider-CrBZ52nR.js.map +0 -1
  76. package/dist/solana-mobile-wallet-provider-CAaGfPZJ.js +0 -579
  77. package/dist/solana-mobile-wallet-provider-CAaGfPZJ.js.map +0 -1
  78. package/dist/solana-mobile-wallet-provider-DGyWHJVI.esm.js +0 -558
  79. package/dist/solana-mobile-wallet-provider-DGyWHJVI.esm.js.map +0 -1
@@ -1,24 +1,9 @@
1
1
  import globalAxios from 'axios';
2
- import { PublicKey, SystemProgram, TransactionInstruction, ComputeBudgetProgram, Keypair, VersionedTransaction, TransactionMessage, Transaction, Connection } from '@solana/web3.js';
2
+ import { PublicKey, SystemProgram, TransactionInstruction, ComputeBudgetProgram, Keypair, VersionedTransaction, TransactionMessage, Transaction, Connection, VersionedMessage } from '@solana/web3.js';
3
3
  import * as anchor from '@coral-xyz/anchor';
4
4
  import { Program } from '@coral-xyz/anchor';
5
5
  import * as React$2 from 'react';
6
6
 
7
- function _mergeNamespaces(n, m) {
8
- m.forEach(function (e) {
9
- e && typeof e !== 'string' && !Array.isArray(e) && Object.keys(e).forEach(function (k) {
10
- if (k !== 'default' && !(k in n)) {
11
- var d = Object.getOwnPropertyDescriptor(e, k);
12
- Object.defineProperty(n, k, d.get ? d : {
13
- enumerable: true,
14
- get: function () { return e[k]; }
15
- });
16
- }
17
- });
18
- });
19
- return Object.freeze(n);
20
- }
21
-
22
7
  function getDefaultExportFromCjs$1 (x) {
23
8
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
24
9
  }
@@ -6764,6 +6749,28 @@ class WebSessionManager {
6764
6749
  static async storeSession(address, accessToken, idToken, refreshToken) {
6765
6750
  if (typeof window === "undefined")
6766
6751
  return;
6752
+ // JWT-wallet binding: refuse to store a session whose idToken is bound
6753
+ // to a different wallet than `address`. Prevents races that would otherwise
6754
+ // leave localStorage with mismatched address/token state.
6755
+ try {
6756
+ const payloadB64 = idToken.split(".")[1];
6757
+ if (payloadB64) {
6758
+ const payload = JSON.parse(this.decodeBase64Url(payloadB64));
6759
+ const tokenWallet = payload["custom:walletAddress"];
6760
+ if (tokenWallet && tokenWallet !== address) {
6761
+ throw new Error(`[WebSessionManager] Refusing to store session: address (${address}) does not match idToken custom:walletAddress (${tokenWallet})`);
6762
+ }
6763
+ if (!tokenWallet) {
6764
+ console.warn("[WebSessionManager] storeSession: idToken has no custom:walletAddress claim — writing without validation");
6765
+ }
6766
+ }
6767
+ }
6768
+ catch (err) {
6769
+ if (typeof (err === null || err === void 0 ? void 0 : err.message) === "string" && err.message.includes("Refusing to store session")) {
6770
+ throw err;
6771
+ }
6772
+ console.warn("[WebSessionManager] storeSession: failed to decode idToken for validation:", err);
6773
+ }
6767
6774
  const config = await getConfig();
6768
6775
  const currentAppId = config.appId;
6769
6776
  localStorage.setItem(this.TAROBASE_SESSION_STORAGE_KEY, JSON.stringify({
@@ -9462,11 +9469,11 @@ function requireSrc$1 () {
9462
9469
  }
9463
9470
 
9464
9471
  var bs58$1;
9465
- var hasRequiredBs58$1;
9472
+ var hasRequiredBs58;
9466
9473
 
9467
- function requireBs58$1 () {
9468
- if (hasRequiredBs58$1) return bs58$1;
9469
- hasRequiredBs58$1 = 1;
9474
+ function requireBs58 () {
9475
+ if (hasRequiredBs58) return bs58$1;
9476
+ hasRequiredBs58 = 1;
9470
9477
  var basex = requireSrc$1();
9471
9478
  var ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
9472
9479
 
@@ -9474,8 +9481,8 @@ function requireBs58$1 () {
9474
9481
  return bs58$1;
9475
9482
  }
9476
9483
 
9477
- var bs58Exports$1 = requireBs58$1();
9478
- var bs58$2 = /*@__PURE__*/getDefaultExportFromCjs(bs58Exports$1);
9484
+ var bs58Exports = requireBs58();
9485
+ var bs58 = /*@__PURE__*/getDefaultExportFromCjs(bs58Exports);
9479
9486
 
9480
9487
  // ─────────────────────────────────────────────────────────────
9481
9488
  // Local implementation of getSimulationComputeUnits
@@ -9737,7 +9744,7 @@ function loadKeypairFromEnv() {
9737
9744
  try {
9738
9745
  const secretKey = secret.trim().startsWith("[")
9739
9746
  ? Uint8Array.from(JSON.parse(secret))
9740
- : bs58$2.decode(secret.trim());
9747
+ : bs58.decode(secret.trim());
9741
9748
  return Keypair.fromSecretKey(secretKey);
9742
9749
  }
9743
9750
  catch (err) {
@@ -11682,6 +11689,28 @@ class ReactNativeSessionManager {
11682
11689
  /* STORE */
11683
11690
  /* ------------------------------------------------------------------ */
11684
11691
  static async storeSession(address, accessToken, idToken, refreshToken) {
11692
+ // JWT-wallet binding: refuse to store a session whose idToken is bound
11693
+ // to a different wallet than `address`. Prevents races that would otherwise
11694
+ // leave storage with mismatched address/token state.
11695
+ try {
11696
+ const payloadB64 = idToken.split(".")[1];
11697
+ if (payloadB64) {
11698
+ const payload = JSON.parse(this.decodeBase64Url(payloadB64));
11699
+ const tokenWallet = payload["custom:walletAddress"];
11700
+ if (tokenWallet && tokenWallet !== address) {
11701
+ throw new Error(`[ReactNativeSessionManager] Refusing to store session: address (${address}) does not match idToken custom:walletAddress (${tokenWallet})`);
11702
+ }
11703
+ if (!tokenWallet) {
11704
+ console.warn("[ReactNativeSessionManager] storeSession: idToken has no custom:walletAddress claim — writing without validation");
11705
+ }
11706
+ }
11707
+ }
11708
+ catch (err) {
11709
+ if (typeof (err === null || err === void 0 ? void 0 : err.message) === "string" && err.message.includes("Refusing to store session")) {
11710
+ throw err;
11711
+ }
11712
+ console.warn("[ReactNativeSessionManager] storeSession: failed to decode idToken for validation:", err);
11713
+ }
11685
11714
  const config = await getConfig();
11686
11715
  const currentAppId = config.appId;
11687
11716
  this.getStorage().setItem(this.TAROBASE_SESSION_STORAGE_KEY, JSON.stringify({
@@ -12788,10 +12817,10 @@ let currentAuthProvider = null;
12788
12817
  let currentAuthMethod = null;
12789
12818
  let initConfig = null;
12790
12819
  // --- localStorage helpers: track which auth method the user last logged in with ---
12791
- const STORED_AUTH_METHOD_KEY = 'tarobase_last_auth_method';
12820
+ const STORED_AUTH_METHOD_KEY$1 = 'tarobase_last_auth_method';
12792
12821
  function getStoredAuthMethod() {
12793
12822
  try {
12794
- return getPlatform().storage.getItem(STORED_AUTH_METHOD_KEY);
12823
+ return getPlatform().storage.getItem(STORED_AUTH_METHOD_KEY$1);
12795
12824
  }
12796
12825
  catch (_a) {
12797
12826
  return null;
@@ -12800,10 +12829,10 @@ function getStoredAuthMethod() {
12800
12829
  function setStoredAuthMethod(method) {
12801
12830
  try {
12802
12831
  if (method) {
12803
- getPlatform().storage.setItem(STORED_AUTH_METHOD_KEY, method);
12832
+ getPlatform().storage.setItem(STORED_AUTH_METHOD_KEY$1, method);
12804
12833
  }
12805
12834
  else {
12806
- getPlatform().storage.removeItem(STORED_AUTH_METHOD_KEY);
12835
+ getPlatform().storage.removeItem(STORED_AUTH_METHOD_KEY$1);
12807
12836
  }
12808
12837
  }
12809
12838
  catch (_a) {
@@ -15680,7 +15709,7 @@ async function loadDependencies() {
15680
15709
  const [reactModule, reactDomModule, phantomModule] = await Promise.all([
15681
15710
  import('react'),
15682
15711
  import('react-dom/client'),
15683
- import('./index-CP_wLmYu.esm.js')
15712
+ import('./index-Buu8v-Oe.esm.js')
15684
15713
  ]);
15685
15714
  // Extract default export from ESM module namespace
15686
15715
  // Dynamic import() returns { default: Module, ...exports }, not the module directly
@@ -15826,6 +15855,17 @@ class PhantomWalletProvider {
15826
15855
  const isMobile = detectMobile();
15827
15856
  const hasPhantomInjected = discoveredWallets.some((w) => w.id === 'phantom');
15828
15857
  const showDeeplink = isMobile && sdkProviders.includes('deeplink') && !hasPhantomInjected;
15858
+ // Treat MWA's own wallet-standard registrations as not-injected, so the MWA
15859
+ // button still appears when only MWA is present (e.g. Android Chrome on Seeker).
15860
+ const hasInjectedWallet = discoveredWallets.some((w) => {
15861
+ var _a, _b;
15862
+ const id = String((_a = w.id) !== null && _a !== void 0 ? _a : '').toLowerCase();
15863
+ const name = String((_b = w.name) !== null && _b !== void 0 ? _b : '').toLowerCase();
15864
+ return (id !== 'mobile-wallet-adapter' &&
15865
+ id !== 'remote-mobile-wallet-adapter' &&
15866
+ name !== 'mobile wallet adapter' &&
15867
+ name !== 'remote mobile wallet adapter');
15868
+ });
15829
15869
  // Track previous modal state to detect closes
15830
15870
  const prevModalOpen = React$1.useRef(false);
15831
15871
  // Track when modal was closed because user selected a wallet (not dismissed)
@@ -15874,6 +15914,16 @@ class PhantomWalletProvider {
15874
15914
  if (!(phantom === null || phantom === void 0 ? void 0 : phantom.isConnected) || (phantom === null || phantom === void 0 ? void 0 : phantom.isLoading) || that.loginInProgress || that.autoLoginInProgress || that.pendingLogin) {
15875
15915
  return;
15876
15916
  }
15917
+ // Don't clobber a session owned by MWA on Seeker. MWA marks the
15918
+ // auth method as soon as login starts (see solana-mobile-wallet-provider),
15919
+ // and clears it on logout, so this guard is correct in both
15920
+ // steady-state and post-logout cases.
15921
+ try {
15922
+ if (getPlatform().storage.getItem('tarobase_last_auth_method') === 'mobile-wallet-adapter') {
15923
+ return;
15924
+ }
15925
+ }
15926
+ catch (_b) { }
15877
15927
  // Need solana to be available AND connected for signing
15878
15928
  if (!solana || !solanaHook.isAvailable || !solana.connected) {
15879
15929
  return;
@@ -15903,12 +15953,21 @@ class PhantomWalletProvider {
15903
15953
  const signatureBytes = signResult.signature;
15904
15954
  const signature = bufferExports.Buffer.from(signatureBytes).toString('base64');
15905
15955
  const createSessionResult = await createSessionWithSignature(publicKey, messageText, signature);
15956
+ // Pre-write guard: MWA may have started a login while we were
15957
+ // in flight. If MWA owns the auth method now, abort before we
15958
+ // overwrite its session (or its in-flight session).
15959
+ try {
15960
+ if (getPlatform().storage.getItem('tarobase_last_auth_method') === 'mobile-wallet-adapter') {
15961
+ return;
15962
+ }
15963
+ }
15964
+ catch (_c) { }
15906
15965
  await WebSessionManager.storeSession(publicKey, createSessionResult.accessToken, createSessionResult.idToken, createSessionResult.refreshToken);
15907
15966
  // Mark auth method so clearIncompatibleSession() doesn't wipe this session
15908
15967
  try {
15909
15968
  getPlatform().storage.setItem('tarobase_last_auth_method', 'phantom');
15910
15969
  }
15911
- catch (_b) { }
15970
+ catch (_d) { }
15912
15971
  setCurrentUser({ provider: that, address: publicKey });
15913
15972
  }
15914
15973
  catch (error) {
@@ -16264,8 +16323,9 @@ class PhantomWalletProvider {
16264
16323
  }), 'Open Phantom app'));
16265
16324
  }
16266
16325
  // Mobile Wallet Adapter button — shown on Android when MWA callback is available
16326
+ // and no injected wallet is present (hides button inside Phantom/Solflare/etc. in-app browsers).
16267
16327
  const isAndroid = detectAndroid();
16268
- if (isAndroid && that.onSwitchToMWA) {
16328
+ if (isAndroid && that.onSwitchToMWA && !hasInjectedWallet) {
16269
16329
  walletButtons.push(React$1.createElement('button', {
16270
16330
  key: 'mobile-wallet',
16271
16331
  style: buttonStyle('mobile-wallet'),
@@ -20307,26 +20367,16 @@ function requireSrc () {
20307
20367
  return src;
20308
20368
  }
20309
20369
 
20310
- var bs58;
20311
- var hasRequiredBs58;
20312
-
20313
- function requireBs58 () {
20314
- if (hasRequiredBs58) return bs58;
20315
- hasRequiredBs58 = 1;
20316
- var basex = requireSrc();
20317
- var ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
20318
-
20319
- bs58 = basex(ALPHABET);
20320
- return bs58;
20321
- }
20370
+ var srcExports = requireSrc();
20371
+ var basex = /*@__PURE__*/getDefaultExportFromCjs$1(srcExports);
20322
20372
 
20323
- var bs58Exports = requireBs58();
20324
- var base58 = /*@__PURE__*/getDefaultExportFromCjs$1(bs58Exports);
20373
+ var ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
20374
+ var base58 = basex(ALPHABET);
20325
20375
 
20326
- var index = /*#__PURE__*/_mergeNamespaces({
20376
+ var index = /*#__PURE__*/Object.freeze({
20327
20377
  __proto__: null,
20328
20378
  default: base58
20329
- }, [bs58Exports]);
20379
+ });
20330
20380
 
20331
20381
  const SURFNET_RPC_URL$1 = "https://surfpool.fly.dev";
20332
20382
  let React;
@@ -21228,26 +21278,135 @@ function isMobileWalletAvailable() {
21228
21278
  return detectAndroid();
21229
21279
  }
21230
21280
  const ED25519_SIGNATURE_LENGTH = 64;
21231
- // Dynamically imported MWA protocol module
21232
- let mwaProtocolModule = null;
21233
- let mwaProtocolLoadPromise = null;
21234
- async function loadMwaProtocol() {
21235
- if (mwaProtocolModule)
21236
- return;
21237
- if (typeof window === 'undefined')
21238
- return;
21239
- if (mwaProtocolLoadPromise)
21240
- return mwaProtocolLoadPromise;
21241
- mwaProtocolLoadPromise = (async () => {
21242
- try {
21243
- mwaProtocolModule = await import('./index.browser-vUinl_9y.esm.js');
21281
+ const STORED_AUTH_METHOD_KEY = 'tarobase_last_auth_method';
21282
+ const MWA_AUTH_METHOD = 'mobile-wallet-adapter';
21283
+ /**
21284
+ * Normalize a chain string to a wallet-standard Solana chain identifier.
21285
+ *
21286
+ * Handles three input shapes that all show up in practice:
21287
+ * - Bare cluster name from `SolanaMobileWalletConfig.cluster` (e.g. 'devnet').
21288
+ * - Prefixed `solana:cluster` (e.g. 'solana:devnet').
21289
+ * - Double-prefixed `solana:solana:cluster` (constructor used to wrap cluster
21290
+ * with `solana:` blindly; if a caller already passed a prefixed string the
21291
+ * result was `solana:solana:devnet`).
21292
+ *
21293
+ * Wallet-standard Solana chains are exactly:
21294
+ * 'solana:mainnet' | 'solana:devnet' | 'solana:testnet' | 'solana:localnet'
21295
+ * — `mainnet-beta` is NOT a valid wallet-standard identifier and must be
21296
+ * normalized to `solana:mainnet`.
21297
+ */
21298
+ function mapChainToWalletStandard(input) {
21299
+ if (!input)
21300
+ return 'solana:mainnet';
21301
+ let s = String(input).toLowerCase();
21302
+ while (s.startsWith('solana:'))
21303
+ s = s.slice('solana:'.length);
21304
+ if (s === 'mainnet-beta' || s === 'mainnet')
21305
+ return 'solana:mainnet';
21306
+ if (s === 'devnet')
21307
+ return 'solana:devnet';
21308
+ if (s === 'testnet')
21309
+ return 'solana:testnet';
21310
+ if (s === 'localnet')
21311
+ return 'solana:localnet';
21312
+ return 'solana:mainnet';
21313
+ }
21314
+ /**
21315
+ * Serialize a Transaction/VersionedTransaction to a Uint8Array wire payload
21316
+ * that the wallet-standard `solana:signTransaction` /
21317
+ * `solana:signAndSendTransaction` features expect.
21318
+ *
21319
+ * Legacy transactions must serialize with `requireAllSignatures: false` and
21320
+ * `verifySignatures: false` — the wallet hasn't signed yet, and the default
21321
+ * `serialize()` would throw "Signature verification failed". Versioned
21322
+ * transactions use the default `serialize()` which already permits unsigned
21323
+ * payloads. Mirrors the helper at
21324
+ * `@solana-mobile/mobile-wallet-adapter-protocol-web3js/lib/esm/index.browser.js:13-18`.
21325
+ */
21326
+ function txToWireBytes(tx) {
21327
+ if ('version' in tx)
21328
+ return tx.serialize();
21329
+ return tx.serialize({ requireAllSignatures: false, verifySignatures: false });
21330
+ }
21331
+ /**
21332
+ * Deserialize a wire-format transaction returned by the wallet
21333
+ * `solana:signTransaction` feature. Mirrors the helper at
21334
+ * `@solana-mobile/mobile-wallet-adapter-protocol-web3js/lib/esm/index.browser.js:19-23`
21335
+ * so legacy and versioned bytes both round-trip correctly.
21336
+ */
21337
+ function txFromWireBytes(byteArray) {
21338
+ const messageOffset = byteArray[0] * ED25519_SIGNATURE_LENGTH + 1;
21339
+ const messageVersion = VersionedMessage.deserializeMessageVersion(byteArray.slice(messageOffset, byteArray.length));
21340
+ if (messageVersion === 'legacy') {
21341
+ return Transaction.from(byteArray);
21342
+ }
21343
+ return VersionedTransaction.deserialize(byteArray);
21344
+ }
21345
+ /**
21346
+ * Per-method runtime narrowing of wallet-standard features. The wallet's
21347
+ * `.features` type is a union (`signAndSendTransaction` | `signTransaction`),
21348
+ * and after authorization the wallet may also narrow `#optionalFeatures`
21349
+ * based on actual wallet capabilities. Narrow at the call site so a method
21350
+ * that needs only `signMessage` doesn't fail when a wallet lacks
21351
+ * `signTransaction`.
21352
+ */
21353
+ function getSignMessageFeature(wallet) {
21354
+ const f = wallet.features;
21355
+ const feat = f['solana:signMessage'];
21356
+ if (!feat || typeof feat.signMessage !== 'function') {
21357
+ throw new Error('Wallet does not support solana:signMessage');
21358
+ }
21359
+ return feat;
21360
+ }
21361
+ function getSignTransactionFeature(wallet) {
21362
+ const f = wallet.features;
21363
+ const feat = f['solana:signTransaction'];
21364
+ if (!feat || typeof feat.signTransaction !== 'function') {
21365
+ throw new Error('Wallet does not support solana:signTransaction');
21366
+ }
21367
+ return feat;
21368
+ }
21369
+ function getSignAndSendTransactionFeature(wallet) {
21370
+ const f = wallet.features;
21371
+ const feat = f['solana:signAndSendTransaction'];
21372
+ if (!feat || typeof feat.signAndSendTransaction !== 'function') {
21373
+ throw new Error('Wallet does not support solana:signAndSendTransaction');
21374
+ }
21375
+ return feat;
21376
+ }
21377
+ function getConnectFeature(wallet) {
21378
+ const f = wallet.features;
21379
+ const feat = f['standard:connect'];
21380
+ if (!feat || typeof feat.connect !== 'function') {
21381
+ throw new Error('Wallet does not support standard:connect');
21382
+ }
21383
+ return feat;
21384
+ }
21385
+ function getDisconnectFeature(wallet) {
21386
+ const f = wallet.features;
21387
+ const feat = f['standard:disconnect'];
21388
+ return feat && typeof feat.disconnect === 'function' ? feat : null;
21389
+ }
21390
+ function writeAuthMethod(method) {
21391
+ try {
21392
+ if (method === null) {
21393
+ getPlatform().storage.removeItem(STORED_AUTH_METHOD_KEY);
21244
21394
  }
21245
- catch (e) {
21246
- console.warn('[SolanaMobileWallet] @solana-mobile/mobile-wallet-adapter-protocol-web3js not installed. Install it to enable Seeker wallet support.');
21247
- throw new Error('Missing @solana-mobile/mobile-wallet-adapter-protocol-web3js dependency');
21395
+ else {
21396
+ getPlatform().storage.setItem(STORED_AUTH_METHOD_KEY, method);
21248
21397
  }
21249
- })();
21250
- return mwaProtocolLoadPromise;
21398
+ }
21399
+ catch (_a) {
21400
+ // storage may be unavailable
21401
+ }
21402
+ }
21403
+ function readAuthMethod() {
21404
+ try {
21405
+ return getPlatform().storage.getItem(STORED_AUTH_METHOD_KEY);
21406
+ }
21407
+ catch (_a) {
21408
+ return null;
21409
+ }
21251
21410
  }
21252
21411
  /**
21253
21412
  * Registers Mobile Wallet Adapter as a wallet-standard wallet so it appears
@@ -21262,7 +21421,7 @@ async function registerMobileWalletAdapter(config) {
21262
21421
  if (typeof window === 'undefined')
21263
21422
  return;
21264
21423
  try {
21265
- const walletStandardMobile = await import('./index.browser-B_wQp2A8.esm.js');
21424
+ const walletStandardMobile = await import('./index.browser-zfGYm0ST.esm.js');
21266
21425
  const registerMwa = walletStandardMobile.registerMwa || ((_a = walletStandardMobile.default) === null || _a === void 0 ? void 0 : _a.registerMwa);
21267
21426
  if (!registerMwa) {
21268
21427
  console.warn('[SolanaMobileWallet] registerMwa not found in @solana-mobile/wallet-standard-mobile');
@@ -21291,32 +21450,41 @@ async function registerMobileWalletAdapter(config) {
21291
21450
  registerMwa(options);
21292
21451
  }
21293
21452
  catch (e) {
21294
- // @solana-mobile/wallet-standard-mobile is an optional dependency
21295
- // Silently skip if not installed — the provider still works via
21296
- // @solana-mobile/mobile-wallet-adapter-protocol-web3js directly
21297
21453
  console.debug('[SolanaMobileWallet] @solana-mobile/wallet-standard-mobile not available, skipping wallet-standard registration');
21298
21454
  }
21299
21455
  }
21300
21456
  /**
21301
- * SolanaMobileWalletProvider implements the AuthProvider interface using the
21302
- * Solana Mobile Wallet Adapter (MWA) protocol's low-level `transact` API.
21457
+ * SolanaMobileWalletProvider implements the AuthProvider interface using
21458
+ * Solana Mobile's wallet-standard wrapper (`LocalSolanaMobileWalletAdapterWallet`).
21303
21459
  *
21304
- * This enables TaroBase dApps to work with any MWA-compliant wallet on Solana
21305
- * mobile devices (Seeker, Saga) — including the native Seed Vault Wallet,
21306
- * Phantom, Solflare, and any other wallet that implements the MWA protocol.
21460
+ * Why wallet-standard and not the raw MWA protocol: as of
21461
+ * `@solana-mobile/wallet-standard-mobile@0.5.0+`, the wallet's internal
21462
+ * `#transact` calls `checkLocalNetworkAccessPermission()` before opening the
21463
+ * localhost WebSocket. That helper renders a three-stage UX flow Solana
21464
+ * Mobile designed specifically to defuse Chrome's Local Network Access
21465
+ * permission dialog (which renders with disabled buttons on Android Chrome,
21466
+ * the source of the long-running Seeker MWA bug):
21307
21467
  *
21308
- * By using `transact()` directly (instead of the wallet-adapter wrapper),
21309
- * login combines authorize + signMessage into a single wallet session —
21310
- * meaning one popup/approval instead of two.
21468
+ * 1. "Allow connections to your wallet" informational modal.
21469
+ * 2. Chrome's permission prompt (interactive because it's spawned as a
21470
+ * fresh user gesture from step 1).
21471
+ * 3. "Ready to connect!" success modal.
21472
+ *
21473
+ * `checkLocalNetworkAccessPermission` is internal to the library — it's not
21474
+ * exported. The only way to invoke it is to go through the wallet's
21475
+ * `standard:connect` / `solana:sign*` features, which is what this provider
21476
+ * does.
21477
+ *
21478
+ * Notes on UX: login uses two wallet popups in succession — `standard:connect`
21479
+ * (authorize) and `solana:signMessage` (sign the Tarobase nonce). The
21480
+ * previous implementation combined both inside a single `transact` callback;
21481
+ * splitting them is the cost of going through wallet-standard, and is
21482
+ * acceptable given the alternative was broken on Seeker.
21311
21483
  */
21312
21484
  class SolanaMobileWalletProvider {
21313
21485
  constructor(networkUrl = null, config = {}) {
21314
- // MWA authorization state
21315
- this.authToken = null;
21316
- this.walletUriBase = null;
21317
- this.base64Address = null;
21318
- this.authorizedPublicKey = null;
21319
- this.publicKeyObj = null;
21486
+ /** LocalSolanaMobileWalletAdapterWallet, lazy-constructed in ensureWallet(). */
21487
+ this.wallet = null;
21320
21488
  this.networkUrl = networkUrl;
21321
21489
  this.config = config;
21322
21490
  if (typeof window === 'undefined') {
@@ -21329,7 +21497,7 @@ class SolanaMobileWalletProvider {
21329
21497
  name: 'TaroBase App',
21330
21498
  uri: getPlatform().getLocationOrigin(),
21331
21499
  };
21332
- this.chain = `solana:${config.cluster || 'mainnet-beta'}`;
21500
+ this.cluster = config.cluster || 'mainnet-beta';
21333
21501
  SolanaMobileWalletProvider.instance = this;
21334
21502
  }
21335
21503
  static getInstance(networkUrl, config) {
@@ -21338,131 +21506,118 @@ class SolanaMobileWalletProvider {
21338
21506
  }
21339
21507
  return SolanaMobileWalletProvider.instance;
21340
21508
  }
21341
- /** Config for transact() routes to the same wallet if we've already authorized */
21342
- getAssociationConfig() {
21343
- if (this.walletUriBase) {
21344
- return { baseUri: this.walletUriBase };
21345
- }
21346
- return undefined;
21509
+ /** Lazy-construct LocalSolanaMobileWalletAdapterWallet on first need. */
21510
+ async ensureWallet() {
21511
+ if (this.wallet)
21512
+ return this.wallet;
21513
+ const mod = await import('./index.browser-zfGYm0ST.esm.js');
21514
+ const chain = mapChainToWalletStandard(this.cluster);
21515
+ this.wallet = new mod.LocalSolanaMobileWalletAdapterWallet({
21516
+ appIdentity: this.appIdentity,
21517
+ authorizationCache: mod.createDefaultAuthorizationCache(),
21518
+ chains: [chain],
21519
+ chainSelector: mod.createDefaultChainSelector(),
21520
+ onWalletNotFound: mod.createDefaultWalletNotFoundHandler(),
21521
+ });
21522
+ return this.wallet;
21347
21523
  }
21348
- /** Reauthorize within a transact callback using a stored auth_token */
21349
- async reauthorizeWallet(wallet) {
21350
- if (!this.authToken) {
21351
- // No existing auth do a fresh authorize
21352
- const authResult = await wallet.authorize({
21353
- identity: this.appIdentity,
21354
- chain: this.chain,
21355
- });
21356
- this.storeAuthResult(authResult);
21357
- return authResult;
21524
+ /**
21525
+ * Ensure the wallet has an active authorization. After a Tarobase session
21526
+ * is restored from storage on cold-start, `wallet.accounts` is empty until
21527
+ * we silently reconnect from the AuthorizationCache. If the cache is also
21528
+ * empty, fall back to interactive `login()` (which surfaces the three-stage
21529
+ * Solana Mobile LNA flow + wallet popups).
21530
+ *
21531
+ * Returns the wallet-standard `WalletAccount` to use for sign operations.
21532
+ */
21533
+ async ensureAuthorized() {
21534
+ const wallet = await this.ensureWallet();
21535
+ if (wallet.accounts.length === 0) {
21536
+ try {
21537
+ const connectFeat = getConnectFeature(wallet);
21538
+ await connectFeat.connect({ silent: true });
21539
+ }
21540
+ catch (e) {
21541
+ console.warn('[SolanaMobileWallet] silent connect failed:', e === null || e === void 0 ? void 0 : e.message);
21542
+ }
21358
21543
  }
21359
- try {
21360
- const authResult = await wallet.reauthorize({
21361
- auth_token: this.authToken,
21362
- identity: this.appIdentity,
21363
- });
21364
- this.storeAuthResult(authResult);
21365
- return authResult;
21366
- }
21367
- catch (e) {
21368
- // Reauth failed (token expired, wallet reset, etc.) — try fresh authorize
21369
- console.warn('[SolanaMobileWallet] Reauthorization failed, attempting fresh authorize:', e === null || e === void 0 ? void 0 : e.message);
21370
- const authResult = await wallet.authorize({
21371
- identity: this.appIdentity,
21372
- chain: this.chain,
21373
- });
21374
- this.storeAuthResult(authResult);
21375
- return authResult;
21544
+ if (wallet.accounts.length === 0) {
21545
+ const user = await this.login();
21546
+ if (!user)
21547
+ throw new Error('MWA not connected');
21376
21548
  }
21549
+ return wallet.accounts[0];
21377
21550
  }
21378
- /** Persist auth result from an authorize/reauthorize call */
21379
- storeAuthResult(authResult) {
21380
- var _a;
21381
- this.authToken = authResult.auth_token;
21382
- this.walletUriBase = authResult.wallet_uri_base;
21383
- if (((_a = authResult.accounts) === null || _a === void 0 ? void 0 : _a.length) > 0) {
21384
- const account = authResult.accounts[0];
21385
- this.base64Address = account.address;
21386
- // Decode base64 address → PublicKey → base58
21387
- const addressBytes = Uint8Array.from(getPlatform().atob(account.address), c => c.charCodeAt(0));
21388
- this.publicKeyObj = new PublicKey(addressBytes);
21389
- this.authorizedPublicKey = this.publicKeyObj.toBase58();
21390
- }
21551
+ /** Returns the active wallet-standard chain identifier (e.g. 'solana:mainnet'). */
21552
+ getChain() {
21553
+ return mapChainToWalletStandard(this.cluster);
21391
21554
  }
21392
21555
  async login() {
21393
21556
  var _a, _b, _c, _d, _e;
21394
21557
  setAuthLoading(true);
21558
+ // Mark the auth method early so a concurrent Phantom auto-create-session
21559
+ // effect (see phantom-wallet-provider) sees 'mobile-wallet-adapter'
21560
+ // during our slow connect/sign roundtrip and backs off. Capture the
21561
+ // previous value so we can restore it if login fails.
21562
+ const prevAuthMethod = readAuthMethod();
21563
+ writeAuthMethod(MWA_AUTH_METHOD);
21395
21564
  try {
21396
- await loadMwaProtocol();
21397
- const { transact } = mwaProtocolModule;
21398
- // Quick check: if we already have auth state and a valid session, skip
21399
- if (this.authorizedPublicKey) {
21565
+ const wallet = await this.ensureWallet();
21566
+ // Quick-check: wallet may already have authorization (e.g. from a
21567
+ // previous in-page login) and a matching Tarobase session skip
21568
+ // popups in that case.
21569
+ if (wallet.accounts.length > 0) {
21570
+ const accountPubkey = new PublicKey(wallet.accounts[0].publicKey);
21571
+ const base58Addr = accountPubkey.toBase58();
21400
21572
  const existingSession = await WebSessionManager.getSession();
21401
- if (existingSession && existingSession.address === this.authorizedPublicKey) {
21402
- const user = { provider: this, address: this.authorizedPublicKey };
21573
+ if (existingSession && existingSession.address === base58Addr) {
21574
+ const user = { provider: this, address: base58Addr };
21403
21575
  setCurrentUser(user);
21404
21576
  return user;
21405
21577
  }
21406
21578
  }
21407
- // Pre-fetch nonce from server while wallet is not yet connected
21579
+ // Pre-fetch nonce while the wallet popup is not yet open.
21408
21580
  const nonce = await genAuthNonce();
21409
- // Single transact() call: authorize + signMessages one wallet popup
21410
- const result = await transact(async (wallet) => {
21411
- // Step 1: Authorize user approves the dApp
21412
- const authResult = await wallet.authorize({
21413
- identity: this.appIdentity,
21414
- chain: this.chain,
21415
- });
21416
- const base64Addr = authResult.accounts[0].address;
21417
- const addressBytes = Uint8Array.from(getPlatform().atob(base64Addr), c => c.charCodeAt(0));
21418
- const pubkey = new PublicKey(addressBytes);
21419
- const base58Addr = pubkey.toBase58();
21420
- // Step 2: Sign the auth message — still in the same wallet session
21421
- const messageText = await genSolanaMessage(base58Addr, nonce);
21422
- const messageBytes = getPlatform().textEncode(messageText);
21423
- const signedMessages = await wallet.signMessages({
21424
- addresses: [base64Addr],
21425
- payloads: [messageBytes],
21426
- });
21427
- // The signed payload is [original message bytes][64-byte ed25519 signature]
21428
- const signedPayload = signedMessages[0];
21429
- const signatureBytes = signedPayload.slice(-ED25519_SIGNATURE_LENGTH);
21430
- return {
21431
- base58Address: base58Addr,
21432
- base64Address: base64Addr,
21433
- publicKey: pubkey,
21434
- signature: bufferExports.Buffer.from(signatureBytes).toString('base64'),
21435
- messageText,
21436
- authToken: authResult.auth_token,
21437
- walletUriBase: authResult.wallet_uri_base,
21438
- };
21439
- }, this.getAssociationConfig());
21440
- // Store MWA auth state for reauthorization in subsequent transact() calls
21441
- this.authToken = result.authToken;
21442
- this.walletUriBase = result.walletUriBase;
21443
- this.base64Address = result.base64Address;
21444
- this.authorizedPublicKey = result.base58Address;
21445
- this.publicKeyObj = result.publicKey;
21446
- // Check if we already have a valid session for this address
21581
+ // Wallet popup #1: standard:connect performs MWA authorize. This
21582
+ // is where wallet-standard's checkLocalNetworkAccessPermission()
21583
+ // fires the three-stage LNA UX (info modal → permission prompt →
21584
+ // success modal) before opening the localhost WebSocket.
21585
+ const connectFeat = getConnectFeature(wallet);
21586
+ const { accounts } = await connectFeat.connect();
21587
+ if (!accounts || accounts.length === 0) {
21588
+ throw new Error('MWA returned no accounts');
21589
+ }
21590
+ const account = accounts[0];
21591
+ const accountPubkey = new PublicKey(account.publicKey);
21592
+ const base58Addr = accountPubkey.toBase58();
21593
+ // If we happen to already have a matching Tarobase session, reuse it.
21447
21594
  const existingSession = await WebSessionManager.getSession();
21448
- if (existingSession && existingSession.address === result.base58Address) {
21449
- const user = { provider: this, address: result.base58Address };
21595
+ if (existingSession && existingSession.address === base58Addr) {
21596
+ const user = { provider: this, address: base58Addr };
21450
21597
  setCurrentUser(user);
21451
21598
  return user;
21452
21599
  }
21453
- // Create new session on the server
21454
- const createSessionResult = await createSessionWithSignature(result.base58Address, result.messageText, result.signature);
21455
- await WebSessionManager.storeSession(result.base58Address, createSessionResult.accessToken, createSessionResult.idToken, createSessionResult.refreshToken);
21456
- // Mark auth method
21457
- try {
21458
- getPlatform().storage.setItem('tarobase_last_auth_method', 'mobile-wallet-adapter');
21600
+ // Wallet popup #2: sign the Tarobase nonce message.
21601
+ const messageText = await genSolanaMessage(base58Addr, nonce);
21602
+ const messageBytes = getPlatform().textEncode(messageText);
21603
+ const signMessageFeat = getSignMessageFeature(wallet);
21604
+ const signResults = await signMessageFeat.signMessage({ account, message: messageBytes });
21605
+ if (!signResults || signResults.length === 0) {
21606
+ throw new Error('MWA returned no signature');
21459
21607
  }
21460
- catch (_f) { }
21461
- const user = { provider: this, address: result.base58Address };
21608
+ const { signature: sigBytes } = signResults[0];
21609
+ const signatureBase64 = bufferExports.Buffer.from(sigBytes).toString('base64');
21610
+ // Create Tarobase session on the server.
21611
+ const createSessionResult = await createSessionWithSignature(base58Addr, messageText, signatureBase64);
21612
+ await WebSessionManager.storeSession(base58Addr, createSessionResult.accessToken, createSessionResult.idToken, createSessionResult.refreshToken);
21613
+ // Auth-method marker is already 'mobile-wallet-adapter' from above.
21614
+ const user = { provider: this, address: base58Addr };
21462
21615
  setCurrentUser(user);
21463
21616
  return user;
21464
21617
  }
21465
21618
  catch (error) {
21619
+ // Restore the previous auth-method marker since this login attempt failed.
21620
+ writeAuthMethod(prevAuthMethod);
21466
21621
  const isUserRejection = (error === null || error === void 0 ? void 0 : error.code) === 4001 ||
21467
21622
  ((_a = error === null || error === void 0 ? void 0 : error.message) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes('user rejected')) ||
21468
21623
  ((_b = error === null || error === void 0 ? void 0 : error.message) === null || _b === void 0 ? void 0 : _b.toLowerCase().includes('user denied')) ||
@@ -21481,55 +21636,42 @@ class SolanaMobileWalletProvider {
21481
21636
  async restoreSession() {
21482
21637
  const session = await WebSessionManager.getSession();
21483
21638
  if (session) {
21484
- this.authorizedPublicKey = session.address;
21485
- this.publicKeyObj = new PublicKey(session.address);
21486
21639
  return { provider: this, address: session.address };
21487
21640
  }
21488
21641
  return null;
21489
21642
  }
21490
21643
  async logout() {
21491
- // Deauthorize with the wallet if we have an auth_token
21492
- if (this.authToken) {
21493
- try {
21494
- await loadMwaProtocol();
21495
- const { transact } = mwaProtocolModule;
21496
- const authToken = this.authToken;
21497
- await transact(async (wallet) => {
21498
- await wallet.deauthorize({ auth_token: authToken });
21499
- }, this.getAssociationConfig());
21500
- }
21501
- catch (error) {
21502
- console.error('[SolanaMobileWallet] Deauthorize error:', error);
21644
+ try {
21645
+ const wallet = await this.ensureWallet();
21646
+ const disconnectFeat = getDisconnectFeature(wallet);
21647
+ if (disconnectFeat) {
21648
+ // Disconnect clears the AuthorizationCache and resets
21649
+ // wallet.#authorization internally (no LNA dialog).
21650
+ await disconnectFeat.disconnect();
21503
21651
  }
21504
21652
  }
21505
- this.authToken = null;
21506
- this.walletUriBase = null;
21507
- this.base64Address = null;
21508
- this.authorizedPublicKey = null;
21509
- this.publicKeyObj = null;
21653
+ catch (error) {
21654
+ console.error('[SolanaMobileWallet] Disconnect error:', error);
21655
+ }
21510
21656
  WebSessionManager.clearSession();
21657
+ // Clear the auth-method marker so Phantom auto-create is unblocked
21658
+ // for any subsequent fresh login on this device.
21659
+ writeAuthMethod(null);
21511
21660
  setCurrentUser(null);
21512
21661
  }
21513
21662
  async signMessage(message) {
21514
21663
  var _a, _b;
21515
- if (!this.authorizedPublicKey || !this.base64Address) {
21516
- throw new Error('Not connected. Call login() first.');
21517
- }
21518
- await loadMwaProtocol();
21519
- const { transact } = mwaProtocolModule;
21520
- const base64Addr = this.base64Address;
21664
+ const account = await this.ensureAuthorized();
21665
+ const wallet = await this.ensureWallet();
21521
21666
  try {
21522
- const signedMessages = await transact(async (wallet) => {
21523
- await this.reauthorizeWallet(wallet);
21524
- const messageBytes = getPlatform().textEncode(message);
21525
- return wallet.signMessages({
21526
- addresses: [base64Addr],
21527
- payloads: [messageBytes],
21528
- });
21529
- }, this.getAssociationConfig());
21530
- const signedPayload = signedMessages[0];
21531
- const signatureBytes = signedPayload.slice(-ED25519_SIGNATURE_LENGTH);
21532
- return bufferExports.Buffer.from(signatureBytes).toString('base64');
21667
+ const signMessageFeat = getSignMessageFeature(wallet);
21668
+ const messageBytes = getPlatform().textEncode(message);
21669
+ const results = await signMessageFeat.signMessage({ account, message: messageBytes });
21670
+ if (!results || results.length === 0) {
21671
+ throw new Error('MWA returned no signature');
21672
+ }
21673
+ const { signature: sigBytes } = results[0];
21674
+ return bufferExports.Buffer.from(sigBytes).toString('base64');
21533
21675
  }
21534
21676
  catch (error) {
21535
21677
  if (((_a = error === null || error === void 0 ? void 0 : error.message) === null || _a === void 0 ? void 0 : _a.includes('not connected')) || ((_b = error === null || error === void 0 ? void 0 : error.message) === null || _b === void 0 ? void 0 : _b.includes('not authorized')) ||
@@ -21542,41 +21684,42 @@ class SolanaMobileWalletProvider {
21542
21684
  }
21543
21685
  async signTransaction(transaction) {
21544
21686
  var _a, _b;
21545
- if (!this.publicKeyObj) {
21546
- throw new Error('Not connected. Call login() first.');
21547
- }
21548
- await loadMwaProtocol();
21549
- const { transact } = mwaProtocolModule;
21550
- // Ensure blockhash and fee payer are set before signing
21687
+ const account = await this.ensureAuthorized();
21688
+ const connectedPubkey = new PublicKey(account.publicKey);
21689
+ const wallet = await this.ensureWallet();
21690
+ const chain = this.getChain();
21691
+ // Preserve existing prep: fill missing blockhash / fee payer so call
21692
+ // sites that build a tx without these fields don't regress.
21551
21693
  const isLegacyTransaction = 'recentBlockhash' in transaction && !('message' in transaction && 'staticAccountKeys' in transaction.message);
21552
21694
  if (isLegacyTransaction) {
21553
21695
  const legacyTx = transaction;
21554
21696
  if (!legacyTx.recentBlockhash) {
21555
- const rpcUrl = this.getRpcUrl();
21556
- const connection = new Connection(rpcUrl, 'confirmed');
21557
- const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');
21697
+ const conn = new Connection(this.getRpcUrl(), 'confirmed');
21698
+ const { blockhash, lastValidBlockHeight } = await conn.getLatestBlockhash('confirmed');
21558
21699
  legacyTx.recentBlockhash = blockhash;
21559
21700
  legacyTx.lastValidBlockHeight = lastValidBlockHeight;
21560
21701
  }
21561
- if (!legacyTx.feePayer && this.publicKeyObj) {
21562
- legacyTx.feePayer = this.publicKeyObj;
21702
+ if (!legacyTx.feePayer) {
21703
+ legacyTx.feePayer = connectedPubkey;
21563
21704
  }
21564
21705
  }
21565
21706
  else {
21566
21707
  const versionedTx = transaction;
21567
21708
  if (!versionedTx.message.recentBlockhash) {
21568
- const rpcUrl = this.getRpcUrl();
21569
- const connection = new Connection(rpcUrl, 'confirmed');
21570
- const { blockhash } = await connection.getLatestBlockhash('confirmed');
21709
+ const conn = new Connection(this.getRpcUrl(), 'confirmed');
21710
+ const { blockhash } = await conn.getLatestBlockhash('confirmed');
21571
21711
  versionedTx.message.recentBlockhash = blockhash;
21572
21712
  }
21573
21713
  }
21574
21714
  try {
21575
- const signedTransactions = await transact(async (wallet) => {
21576
- await this.reauthorizeWallet(wallet);
21577
- return wallet.signTransactions({ transactions: [transaction] });
21578
- }, this.getAssociationConfig());
21579
- return signedTransactions[0];
21715
+ const signTxFeat = getSignTransactionFeature(wallet);
21716
+ const wireBytes = txToWireBytes(transaction);
21717
+ const results = await signTxFeat.signTransaction({ account, transaction: wireBytes, chain });
21718
+ if (!results || results.length === 0) {
21719
+ throw new Error('MWA returned no signed transaction');
21720
+ }
21721
+ const { signedTransaction: signedBytes } = results[0];
21722
+ return txFromWireBytes(new Uint8Array(signedBytes));
21580
21723
  }
21581
21724
  catch (error) {
21582
21725
  if (((_a = error === null || error === void 0 ? void 0 : error.message) === null || _a === void 0 ? void 0 : _a.includes('not connected')) || ((_b = error === null || error === void 0 ? void 0 : error.message) === null || _b === void 0 ? void 0 : _b.includes('not authorized')) ||
@@ -21589,15 +21732,15 @@ class SolanaMobileWalletProvider {
21589
21732
  }
21590
21733
  async signAndSubmitTransaction(transaction, feePayer) {
21591
21734
  var _a, _b, _c, _d, _e, _f;
21592
- if (!this.publicKeyObj) {
21593
- throw new Error('Not connected. Call login() first.');
21594
- }
21595
- await loadMwaProtocol();
21596
- const { transact } = mwaProtocolModule;
21735
+ const account = await this.ensureAuthorized();
21736
+ const connectedPubkey = new PublicKey(account.publicKey);
21737
+ const wallet = await this.ensureWallet();
21738
+ const chain = this.getChain();
21597
21739
  const rpcUrl = this.getRpcUrl();
21598
21740
  const connection = new Connection(rpcUrl, 'confirmed');
21599
21741
  const isSurfnet = rpcUrl === SURFNET_RPC_URL$2;
21600
21742
  try {
21743
+ // Preserve existing prep: refresh blockhash and set fee payer.
21601
21744
  const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');
21602
21745
  const isLegacyTransaction = 'recentBlockhash' in transaction && !('message' in transaction && 'staticAccountKeys' in transaction.message);
21603
21746
  if (isLegacyTransaction) {
@@ -21605,12 +21748,7 @@ class SolanaMobileWalletProvider {
21605
21748
  legacyTx.recentBlockhash = blockhash;
21606
21749
  legacyTx.lastValidBlockHeight = lastValidBlockHeight;
21607
21750
  if (!legacyTx.feePayer) {
21608
- if (feePayer) {
21609
- legacyTx.feePayer = feePayer;
21610
- }
21611
- else if (this.publicKeyObj) {
21612
- legacyTx.feePayer = this.publicKeyObj;
21613
- }
21751
+ legacyTx.feePayer = feePayer !== null && feePayer !== void 0 ? feePayer : connectedPubkey;
21614
21752
  }
21615
21753
  }
21616
21754
  else {
@@ -21618,14 +21756,19 @@ class SolanaMobileWalletProvider {
21618
21756
  versionedTx.message.recentBlockhash = blockhash;
21619
21757
  }
21620
21758
  if (isSurfnet) {
21621
- // Surfnet: sign-only, then submit to our specific RPC
21622
- const signedTransactions = await transact(async (wallet) => {
21623
- await this.reauthorizeWallet(wallet);
21624
- return wallet.signTransactions({ transactions: [transaction] });
21625
- }, this.getAssociationConfig());
21626
- const signedTx = signedTransactions[0];
21759
+ // Surfnet: sign locally via wallet-standard, submit manually
21760
+ // to the Surfnet RPC. Don't route through signAndSendTransaction
21761
+ // because the wallet would submit to its own RPC.
21762
+ const signTxFeat = getSignTransactionFeature(wallet);
21763
+ const wireBytes = txToWireBytes(transaction);
21764
+ const results = await signTxFeat.signTransaction({ account, transaction: wireBytes, chain });
21765
+ if (!results || results.length === 0) {
21766
+ throw new Error('MWA returned no signed transaction');
21767
+ }
21768
+ const { signedTransaction: signedBytes } = results[0];
21769
+ const signedTx = txFromWireBytes(new Uint8Array(signedBytes));
21627
21770
  const signature = await connection.sendRawTransaction(signedTx.serialize(), {
21628
- preflightCommitment: 'confirmed'
21771
+ preflightCommitment: 'confirmed',
21629
21772
  });
21630
21773
  const confirmation = await connection.confirmTransaction({
21631
21774
  signature,
@@ -21637,15 +21780,20 @@ class SolanaMobileWalletProvider {
21637
21780
  }
21638
21781
  return signature;
21639
21782
  }
21640
- // Non-surfnet: use signAndSendTransactions for wallet-optimized submission
21641
- const signatures = await transact(async (wallet) => {
21642
- await this.reauthorizeWallet(wallet);
21643
- return wallet.signAndSendTransactions({
21644
- transactions: [transaction],
21645
- commitment: 'confirmed',
21646
- });
21647
- }, this.getAssociationConfig());
21648
- const signature = signatures[0];
21783
+ // Non-Surfnet: wallet signs and submits to its own RPC.
21784
+ const signSendFeat = getSignAndSendTransactionFeature(wallet);
21785
+ const wireBytes = txToWireBytes(transaction);
21786
+ const results = await signSendFeat.signAndSendTransaction({
21787
+ account,
21788
+ transaction: wireBytes,
21789
+ chain,
21790
+ options: { commitment: 'confirmed' },
21791
+ });
21792
+ if (!results || results.length === 0) {
21793
+ throw new Error('MWA returned no signature');
21794
+ }
21795
+ const { signature: sigBytes } = results[0];
21796
+ const signature = base58.encode(sigBytes);
21649
21797
  await confirmAndCheckTransaction(connection, signature);
21650
21798
  return signature;
21651
21799
  }
@@ -21671,16 +21819,14 @@ class SolanaMobileWalletProvider {
21671
21819
  if (!solTransactionData) {
21672
21820
  throw new Error('Solana transaction data is required for mobile wallet');
21673
21821
  }
21674
- if (!this.publicKeyObj || !this.authorizedPublicKey) {
21675
- await this.login();
21676
- }
21677
- await loadMwaProtocol();
21678
- const { transact } = mwaProtocolModule;
21822
+ const account = await this.ensureAuthorized();
21823
+ const connectedPubkey = new PublicKey(account.publicKey);
21824
+ const wallet = await this.ensureWallet();
21825
+ const chain = this.getChain();
21679
21826
  const rpcUrl = this.getRpcUrl(solTransactionData.network);
21680
21827
  const connection = new Connection(rpcUrl, 'confirmed');
21681
21828
  const isSurfnet = rpcUrl === SURFNET_RPC_URL$2;
21682
21829
  try {
21683
- const publicKey = this.publicKeyObj;
21684
21830
  const remainingAccounts = convertRemainingAccounts(solTransactionData.txArgs[0].remainingAccounts);
21685
21831
  let app_id = solTransactionData.appId;
21686
21832
  if (typeof window !== 'undefined' && window.CUSTOM_TAROBASE_APP_ID_HEADER) {
@@ -21689,9 +21835,9 @@ class SolanaMobileWalletProvider {
21689
21835
  if (!app_id) {
21690
21836
  throw new Error('App ID is required');
21691
21837
  }
21692
- // Create a mock wallet adapter for Anchor (only publicKey is needed for building TX)
21838
+ // Mock wallet adapter for Anchor only publicKey is read during build.
21693
21839
  const mockWalletAdapter = {
21694
- publicKey,
21840
+ publicKey: connectedPubkey,
21695
21841
  signTransaction: async (tx) => tx,
21696
21842
  signAllTransactions: async (txs) => txs,
21697
21843
  };
@@ -21709,10 +21855,12 @@ class SolanaMobileWalletProvider {
21709
21855
  }
21710
21856
  let tx;
21711
21857
  if (solTransactionData.signedTransaction) {
21858
+ // Server has co-signed a CPI attestation. Do NOT mutate its
21859
+ // blockhash — that would invalidate the server's signature.
21712
21860
  tx = VersionedTransaction.deserialize(bufferExports.Buffer.from(solTransactionData.signedTransaction, 'base64'));
21713
21861
  }
21714
21862
  else {
21715
- const result = await buildSetDocumentsTransaction(connection, solTransactionData.txArgs[0].idl, anchorProvider, publicKey, {
21863
+ const result = await buildSetDocumentsTransaction(connection, solTransactionData.txArgs[0].idl, anchorProvider, connectedPubkey, {
21716
21864
  app_id,
21717
21865
  documents: solTransactionData.txArgs[0].setDocumentData,
21718
21866
  delete_paths: solTransactionData.txArgs[0].deletePaths,
@@ -21720,11 +21868,16 @@ class SolanaMobileWalletProvider {
21720
21868
  }, finalDeduped, solTransactionData.lutKey, solTransactionData.preInstructions, false, solTransactionData.additionalLutAddresses);
21721
21869
  tx = result.tx;
21722
21870
  }
21871
+ // Sign-only branch — sign the tx but don't submit.
21723
21872
  if ((options === null || options === void 0 ? void 0 : options.shouldSubmitTx) === false) {
21724
- const [signedTx] = await transact(async (wallet) => {
21725
- await this.reauthorizeWallet(wallet);
21726
- return wallet.signTransactions({ transactions: [tx] });
21727
- }, this.getAssociationConfig());
21873
+ const signTxFeat = getSignTransactionFeature(wallet);
21874
+ const wireBytes = txToWireBytes(tx);
21875
+ const results = await signTxFeat.signTransaction({ account, transaction: wireBytes, chain });
21876
+ if (!results || results.length === 0) {
21877
+ throw new Error('MWA returned no signed transaction');
21878
+ }
21879
+ const { signedTransaction: signedBytes } = results[0];
21880
+ const signedTx = txFromWireBytes(new Uint8Array(signedBytes));
21728
21881
  return {
21729
21882
  signedTransaction: signedTx,
21730
21883
  blockNumber: 0,
@@ -21733,14 +21886,18 @@ class SolanaMobileWalletProvider {
21733
21886
  };
21734
21887
  }
21735
21888
  if (isSurfnet) {
21736
- // Surfnet: sign then submit manually to our RPC
21737
- const [signedTx] = await transact(async (wallet) => {
21738
- await this.reauthorizeWallet(wallet);
21739
- return wallet.signTransactions({ transactions: [tx] });
21740
- }, this.getAssociationConfig());
21889
+ // Surfnet: sign locally via wallet-standard, submit manually.
21890
+ const signTxFeat = getSignTransactionFeature(wallet);
21891
+ const wireBytes = txToWireBytes(tx);
21892
+ const results = await signTxFeat.signTransaction({ account, transaction: wireBytes, chain });
21893
+ if (!results || results.length === 0) {
21894
+ throw new Error('MWA returned no signed transaction');
21895
+ }
21896
+ const { signedTransaction: signedBytes } = results[0];
21897
+ const signedTx = txFromWireBytes(new Uint8Array(signedBytes));
21741
21898
  const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');
21742
21899
  const signature = await connection.sendRawTransaction(signedTx.serialize(), {
21743
- preflightCommitment: 'confirmed'
21900
+ preflightCommitment: 'confirmed',
21744
21901
  });
21745
21902
  const confirmation = await connection.confirmTransaction({
21746
21903
  signature,
@@ -21752,7 +21909,7 @@ class SolanaMobileWalletProvider {
21752
21909
  }
21753
21910
  const txInfo = await connection.getParsedTransaction(signature, {
21754
21911
  maxSupportedTransactionVersion: 0,
21755
- commitment: 'confirmed'
21912
+ commitment: 'confirmed',
21756
21913
  });
21757
21914
  return {
21758
21915
  transactionSignature: signature,
@@ -21761,15 +21918,20 @@ class SolanaMobileWalletProvider {
21761
21918
  data: txInfo === null || txInfo === void 0 ? void 0 : txInfo.meta,
21762
21919
  };
21763
21920
  }
21764
- // Non-surfnet: use signAndSendTransactions
21765
- const signatures = await transact(async (wallet) => {
21766
- await this.reauthorizeWallet(wallet);
21767
- return wallet.signAndSendTransactions({
21768
- transactions: [tx],
21769
- commitment: 'confirmed',
21770
- });
21771
- }, this.getAssociationConfig());
21772
- const signature = signatures[0];
21921
+ // Non-Surfnet: wallet signs and submits to its own RPC.
21922
+ const signSendFeat = getSignAndSendTransactionFeature(wallet);
21923
+ const wireBytes = txToWireBytes(tx);
21924
+ const results = await signSendFeat.signAndSendTransaction({
21925
+ account,
21926
+ transaction: wireBytes,
21927
+ chain,
21928
+ options: { commitment: 'confirmed' },
21929
+ });
21930
+ if (!results || results.length === 0) {
21931
+ throw new Error('MWA returned no signature');
21932
+ }
21933
+ const { signature: sigBytes } = results[0];
21934
+ const signature = base58.encode(sigBytes);
21773
21935
  const txInfo = await confirmAndCheckTransaction(connection, signature);
21774
21936
  return {
21775
21937
  transactionSignature: signature,
@@ -21796,11 +21958,36 @@ class SolanaMobileWalletProvider {
21796
21958
  }
21797
21959
  }
21798
21960
  async getNativeMethods() {
21961
+ var _a, _b, _c;
21962
+ // Synchronous-ish state read; tolerate the wallet being unauthorized
21963
+ // (return nulls). Don't trigger ensureAuthorized() here — callers that
21964
+ // need the wallet active should sign through one of the sign methods.
21965
+ const wallet = this.wallet;
21966
+ if (!wallet) {
21967
+ return {
21968
+ authToken: null,
21969
+ walletUriBase: null,
21970
+ publicKey: null,
21971
+ base64Address: null,
21972
+ wallet: null,
21973
+ currentAuthorization: null,
21974
+ };
21975
+ }
21976
+ const acct = (_a = wallet.accounts) === null || _a === void 0 ? void 0 : _a[0];
21977
+ const auth = wallet.currentAuthorization;
21978
+ let publicKey = null;
21979
+ let base64Address = null;
21980
+ if (acct) {
21981
+ publicKey = new PublicKey(acct.publicKey);
21982
+ base64Address = btoa(String.fromCharCode(...acct.publicKey));
21983
+ }
21799
21984
  return {
21800
- authToken: this.authToken,
21801
- walletUriBase: this.walletUriBase,
21802
- publicKey: this.publicKeyObj,
21803
- base64Address: this.base64Address,
21985
+ authToken: (_b = auth === null || auth === void 0 ? void 0 : auth.auth_token) !== null && _b !== void 0 ? _b : null,
21986
+ walletUriBase: (_c = auth === null || auth === void 0 ? void 0 : auth.wallet_uri_base) !== null && _c !== void 0 ? _c : null,
21987
+ publicKey,
21988
+ base64Address,
21989
+ wallet,
21990
+ currentAuthorization: auth !== null && auth !== void 0 ? auth : null,
21804
21991
  };
21805
21992
  }
21806
21993
  /* ----------------------------------------------------------- *
@@ -22171,4 +22358,4 @@ class PrivyExpoProvider {
22171
22358
  }
22172
22359
 
22173
22360
  export { getCachedData as $, subscribe as A, useAuth as B, deserializeTransaction as C, getIdToken as D, setPlatform as E, getPlatform as F, PrivyWalletProvider as G, DEFAULT_TEST_ADDRESS as H, isMobileWalletAvailable as I, registerMobileWalletAdapter as J, PrivyExpoProvider as K, InsufficientBalanceError as L, MockAuthProvider as M, ServerSessionManager as N, OffchainAuthProvider as O, PhantomWalletProvider as P, buildSetDocumentsTransaction as Q, ReactNativeSessionManager as R, SolanaMobileWalletProvider as S, clearCache as T, closeAllSubscriptions as U, convertRemainingAccounts as V, WebSessionManager as W, createSessionWithPrivy as X, createSessionWithSignature as Y, genAuthNonce as Z, genSolanaMessage as _, base58 as a, getMany as a0, reconnectWithNewAuth as a1, refreshSession as a2, signSessionCreateMessage as a3, bufferExports as b, getCurrentUser as c, onAuthLoadingChanged as d, getAuthLoading as e, logout as f, getDefaultExportFromCjs$1 as g, getConfig as h, init as i, getAuthProvider as j, get as k, login as l, setMany as m, setFile as n, onAuthStateChanged as o, getFiles as p, runQueryMany as q, runQuery as r, set as s, runExpression as t, runExpressionMany as u, signMessage as v, signTransaction as w, signAndSubmitTransaction as x, count as y, aggregate as z };
22174
- //# sourceMappingURL=index-B7yaLhND.esm.js.map
22361
+ //# sourceMappingURL=index-ZKzq5QT_.esm.js.map