@pooflabs/web 0.0.67 → 0.0.69-rc.1

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.
@@ -10620,8 +10620,6 @@ async function getOrCreateConnection(appId, isServer) {
10620
10620
  isConnected: false,
10621
10621
  appId,
10622
10622
  tokenRefreshTimer: null,
10623
- lastMessageAt: Date.now(),
10624
- keepaliveTimer: null,
10625
10623
  consecutiveAuthFailures: 0,
10626
10624
  };
10627
10625
  connections.set(appId, connection);
@@ -10673,7 +10671,6 @@ async function getOrCreateConnection(appId, isServer) {
10673
10671
  ws.addEventListener('open', () => {
10674
10672
  connection.isConnecting = false;
10675
10673
  connection.isConnected = true;
10676
- connection.lastMessageAt = Date.now();
10677
10674
  connection.consecutiveAuthFailures = 0;
10678
10675
  // Schedule periodic token freshness checks
10679
10676
  scheduleTokenRefresh(connection, isServer);
@@ -10682,25 +10679,9 @@ async function getOrCreateConnection(appId, isServer) {
10682
10679
  sub.lastData = undefined;
10683
10680
  sendSubscribe(connection, sub);
10684
10681
  }
10685
- // Start keepalive detection — if no messages for 90s, force reconnect
10686
- if (connection.keepaliveTimer) {
10687
- clearInterval(connection.keepaliveTimer);
10688
- }
10689
- connection.keepaliveTimer = setInterval(() => {
10690
- var _a;
10691
- if (Date.now() - connection.lastMessageAt > 90000) {
10692
- console.warn('[WS v2] No messages received for 90s, forcing reconnect');
10693
- if (connection.keepaliveTimer) {
10694
- clearInterval(connection.keepaliveTimer);
10695
- connection.keepaliveTimer = null;
10696
- }
10697
- (_a = connection.ws) === null || _a === void 0 ? void 0 : _a.reconnect();
10698
- }
10699
- }, 30000);
10700
10682
  });
10701
10683
  // Handle incoming messages
10702
10684
  ws.addEventListener('message', (event) => {
10703
- connection.lastMessageAt = Date.now();
10704
10685
  try {
10705
10686
  const message = JSON.parse(event.data);
10706
10687
  handleServerMessage(connection, message);
@@ -10724,10 +10705,6 @@ async function getOrCreateConnection(appId, isServer) {
10724
10705
  clearInterval(connection.tokenRefreshTimer);
10725
10706
  connection.tokenRefreshTimer = null;
10726
10707
  }
10727
- if (connection.keepaliveTimer) {
10728
- clearInterval(connection.keepaliveTimer);
10729
- connection.keepaliveTimer = null;
10730
- }
10731
10708
  });
10732
10709
  return connection;
10733
10710
  }
@@ -10800,7 +10777,10 @@ function handleServerMessage(connection, message) {
10800
10777
  }
10801
10778
  function notifyCallbacks(subscription, data) {
10802
10779
  var _a;
10803
- for (const callback of subscription.callbacks) {
10780
+ // Snapshot the callbacks array so that unsubscriptions during
10781
+ // notification don't cause callbacks to be skipped.
10782
+ const callbacks = subscription.callbacks.slice();
10783
+ for (const callback of callbacks) {
10804
10784
  try {
10805
10785
  (_a = callback.onData) === null || _a === void 0 ? void 0 : _a.call(callback, data);
10806
10786
  }
@@ -10985,10 +10965,6 @@ async function closeAllSubscriptionsV2() {
10985
10965
  clearInterval(connection.tokenRefreshTimer);
10986
10966
  connection.tokenRefreshTimer = null;
10987
10967
  }
10988
- if (connection.keepaliveTimer) {
10989
- clearInterval(connection.keepaliveTimer);
10990
- connection.keepaliveTimer = null;
10991
- }
10992
10968
  if (connection.ws) {
10993
10969
  const ws = connection.ws;
10994
10970
  connection.ws = null;
@@ -13590,7 +13566,7 @@ async function loadDependencies() {
13590
13566
  const [reactModule, reactDomModule, phantomModule] = await Promise.all([
13591
13567
  import('react'),
13592
13568
  import('react-dom/client'),
13593
- Promise.resolve().then(function () { return require('./index-BHU0Q8vq.js'); })
13569
+ Promise.resolve().then(function () { return require('./index-BRZ4-fcO.js'); })
13594
13570
  ]);
13595
13571
  // Extract default export from ESM module namespace
13596
13572
  // Dynamic import() returns { default: Module, ...exports }, not the module directly
@@ -13702,9 +13678,15 @@ class PhantomWalletProvider {
13702
13678
  this.containerElement.setAttribute('data-phantom-provider', 'true');
13703
13679
  // Keep Phantom UI above host overlays while avoiding global click-capture.
13704
13680
  this.containerElement.style.position = 'fixed';
13705
- this.containerElement.style.inset = '0';
13706
13681
  this.containerElement.style.zIndex = '2147483647';
13707
- this.containerElement.style.pointerEvents = 'none';
13682
+ if (this.config.enablePrivyFallback) {
13683
+ // Full-viewport overlay for custom modal; pointer-events: none lets
13684
+ // clicks pass through to page except where the modal sets 'auto'
13685
+ this.containerElement.style.inset = '0';
13686
+ this.containerElement.style.pointerEvents = 'none';
13687
+ }
13688
+ // When Privy fallback is off, the container just hosts PhantomProvider
13689
+ // without covering the viewport or blocking pointer events
13708
13690
  document.body.appendChild(this.containerElement);
13709
13691
  const that = this;
13710
13692
  const { PhantomProvider: ReactPhantomProvider, usePhantom, useConnect, useDisconnect, useModal, useSolana, useDiscoveredWallets, AddressType, darkTheme, lightTheme } = phantomReactSdk;
@@ -34924,6 +34906,508 @@ class OffchainAuthProvider {
34924
34906
  }
34925
34907
  }
34926
34908
 
34909
+ /**
34910
+ * Detects whether the current environment is a mobile browser capable of
34911
+ * Mobile Wallet Adapter (MWA) communication.
34912
+ *
34913
+ * Returns true on Android browsers (Chrome, etc.) where MWA intents work,
34914
+ * or inside a Seeker/Saga in-app browser context.
34915
+ */
34916
+ function isMobileWalletAvailable() {
34917
+ if (typeof window === 'undefined' || typeof navigator === 'undefined')
34918
+ return false;
34919
+ const ua = navigator.userAgent || '';
34920
+ // Android browser — MWA uses Android intents
34921
+ const isAndroid = /Android/i.test(ua);
34922
+ // In-app browser inside a wallet app on Seeker/Saga
34923
+ const isWalletInAppBrowser = /SolanaWallet/i.test(ua) || /SeedVault/i.test(ua);
34924
+ return isAndroid || isWalletInAppBrowser;
34925
+ }
34926
+ // Dynamically imported MWA module
34927
+ let mwaModule = null;
34928
+ let mwaLoadPromise = null;
34929
+ async function loadMwaModule() {
34930
+ if (mwaModule)
34931
+ return;
34932
+ if (typeof window === 'undefined')
34933
+ return;
34934
+ if (mwaLoadPromise)
34935
+ return mwaLoadPromise;
34936
+ mwaLoadPromise = (async () => {
34937
+ try {
34938
+ mwaModule = await import('@solana-mobile/wallet-adapter-mobile');
34939
+ }
34940
+ catch (e) {
34941
+ console.warn('[SolanaMobileWallet] @solana-mobile/wallet-adapter-mobile not installed. Install it to enable Seeker wallet support.');
34942
+ throw new Error('Missing @solana-mobile/wallet-adapter-mobile dependency');
34943
+ }
34944
+ })();
34945
+ return mwaLoadPromise;
34946
+ }
34947
+ /**
34948
+ * Registers Mobile Wallet Adapter as a wallet-standard wallet so it appears
34949
+ * in wallet selection UIs and can be discovered by other wallet-standard consumers.
34950
+ *
34951
+ * Call this once at app startup (in a browser-only / non-SSR context).
34952
+ *
34953
+ * @param config - App identity and optional remote host authority for desktop QR code support
34954
+ */
34955
+ async function registerMobileWalletAdapter(config) {
34956
+ var _a;
34957
+ if (typeof window === 'undefined')
34958
+ return;
34959
+ try {
34960
+ const walletStandardMobile = await import('@solana-mobile/wallet-standard-mobile');
34961
+ const registerMwa = walletStandardMobile.registerMwa || ((_a = walletStandardMobile.default) === null || _a === void 0 ? void 0 : _a.registerMwa);
34962
+ if (!registerMwa) {
34963
+ console.warn('[SolanaMobileWallet] registerMwa not found in @solana-mobile/wallet-standard-mobile');
34964
+ return;
34965
+ }
34966
+ const options = {};
34967
+ if (config === null || config === void 0 ? void 0 : config.appIdentity) {
34968
+ options.appIdentity = config.appIdentity;
34969
+ }
34970
+ if (config === null || config === void 0 ? void 0 : config.chains) {
34971
+ options.chains = config.chains;
34972
+ }
34973
+ if (config === null || config === void 0 ? void 0 : config.remoteHostAuthority) {
34974
+ options.remoteHostAuthority = config.remoteHostAuthority;
34975
+ }
34976
+ // Use the library's default helpers if available
34977
+ if (walletStandardMobile.createDefaultAuthorizationCache) {
34978
+ options.authorizationCache = walletStandardMobile.createDefaultAuthorizationCache();
34979
+ }
34980
+ if (walletStandardMobile.createDefaultChainSelector) {
34981
+ options.chainSelector = walletStandardMobile.createDefaultChainSelector();
34982
+ }
34983
+ if (walletStandardMobile.createDefaultWalletNotFoundHandler) {
34984
+ options.onWalletNotFound = walletStandardMobile.createDefaultWalletNotFoundHandler();
34985
+ }
34986
+ registerMwa(options);
34987
+ }
34988
+ catch (e) {
34989
+ // @solana-mobile/wallet-standard-mobile is an optional dependency
34990
+ // Silently skip if not installed — the provider still works via
34991
+ // @solana-mobile/wallet-adapter-mobile directly
34992
+ console.debug('[SolanaMobileWallet] @solana-mobile/wallet-standard-mobile not available, skipping wallet-standard registration');
34993
+ }
34994
+ }
34995
+ /**
34996
+ * SolanaMobileWalletProvider implements the AuthProvider interface using the
34997
+ * Solana Mobile Wallet Adapter (MWA) protocol.
34998
+ *
34999
+ * This enables TaroBase dApps to work with any MWA-compliant wallet on Solana
35000
+ * mobile devices (Seeker, Saga) — including the native Seed Vault Wallet,
35001
+ * Phantom, Solflare, and any other wallet that implements the MWA protocol.
35002
+ *
35003
+ * The MWA protocol communicates with wallet apps via Android intents (on mobile)
35004
+ * or WebSocket relay (on desktop via QR code), eliminating the need to integrate
35005
+ * with each wallet individually.
35006
+ */
35007
+ class SolanaMobileWalletProvider {
35008
+ constructor(networkUrl = null, config = {}) {
35009
+ this.mwaAdapter = null;
35010
+ this.authorizedPublicKey = null;
35011
+ this.networkUrl = networkUrl;
35012
+ this.config = config;
35013
+ if (typeof window === 'undefined') {
35014
+ throw new Error('SolanaMobileWalletProvider can only be instantiated in a browser environment');
35015
+ }
35016
+ if (SolanaMobileWalletProvider.instance) {
35017
+ return SolanaMobileWalletProvider.instance;
35018
+ }
35019
+ SolanaMobileWalletProvider.instance = this;
35020
+ }
35021
+ static getInstance(networkUrl, config) {
35022
+ if (!SolanaMobileWalletProvider.instance) {
35023
+ new SolanaMobileWalletProvider(networkUrl, config);
35024
+ }
35025
+ return SolanaMobileWalletProvider.instance;
35026
+ }
35027
+ async ensureAdapter() {
35028
+ var _a;
35029
+ if ((_a = this.mwaAdapter) === null || _a === void 0 ? void 0 : _a.connected)
35030
+ return this.mwaAdapter;
35031
+ await loadMwaModule();
35032
+ const { SolanaMobileWalletAdapter, createDefaultAddressSelector, createDefaultAuthorizationResultCache } = mwaModule;
35033
+ if (!this.mwaAdapter) {
35034
+ const cluster = this.config.cluster || 'mainnet-beta';
35035
+ this.mwaAdapter = new SolanaMobileWalletAdapter({
35036
+ addressSelector: createDefaultAddressSelector(),
35037
+ appIdentity: this.config.appIdentity || {
35038
+ name: 'TaroBase App',
35039
+ uri: typeof window !== 'undefined' ? window.location.origin : undefined,
35040
+ },
35041
+ authorizationResultCache: createDefaultAuthorizationResultCache(),
35042
+ cluster,
35043
+ });
35044
+ }
35045
+ return this.mwaAdapter;
35046
+ }
35047
+ async login() {
35048
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
35049
+ setAuthLoading(true);
35050
+ try {
35051
+ const adapter = await this.ensureAdapter();
35052
+ // Connect triggers the MWA authorization flow — this opens the wallet app
35053
+ // via Android intent and asks the user to authorize this dApp
35054
+ await adapter.connect();
35055
+ const publicKey = ((_b = (_a = adapter.publicKey) === null || _a === void 0 ? void 0 : _a.toBase58) === null || _b === void 0 ? void 0 : _b.call(_a)) || ((_d = (_c = adapter.publicKey) === null || _c === void 0 ? void 0 : _c.toString) === null || _d === void 0 ? void 0 : _d.call(_c));
35056
+ if (!publicKey) {
35057
+ throw new Error('No public key returned from wallet');
35058
+ }
35059
+ this.authorizedPublicKey = publicKey;
35060
+ // Check if we already have a valid session
35061
+ const existingSession = await WebSessionManager.getSession();
35062
+ if (existingSession && existingSession.address === publicKey) {
35063
+ const user = { provider: this, address: publicKey };
35064
+ setCurrentUser(user);
35065
+ return user;
35066
+ }
35067
+ // Create new session with signature
35068
+ const nonce = await genAuthNonce();
35069
+ const messageText = await genSolanaMessage(publicKey, nonce);
35070
+ // MWA signMessage expects Uint8Array
35071
+ const messageBytes = new TextEncoder().encode(messageText);
35072
+ const signatureBytes = await adapter.signMessage(messageBytes);
35073
+ const signature = bufferExports.Buffer.from(signatureBytes).toString('base64');
35074
+ const createSessionResult = await createSessionWithSignature(publicKey, messageText, signature);
35075
+ await WebSessionManager.storeSession(publicKey, createSessionResult.accessToken, createSessionResult.idToken, createSessionResult.refreshToken);
35076
+ // Mark auth method
35077
+ try {
35078
+ localStorage.setItem('tarobase_last_auth_method', 'mobile-wallet-adapter');
35079
+ }
35080
+ catch (_k) { }
35081
+ const user = { provider: this, address: publicKey };
35082
+ setCurrentUser(user);
35083
+ return user;
35084
+ }
35085
+ catch (error) {
35086
+ const isUserRejection = (error === null || error === void 0 ? void 0 : error.code) === 4001 ||
35087
+ ((_e = error === null || error === void 0 ? void 0 : error.message) === null || _e === void 0 ? void 0 : _e.toLowerCase().includes('user rejected')) ||
35088
+ ((_f = error === null || error === void 0 ? void 0 : error.message) === null || _f === void 0 ? void 0 : _f.toLowerCase().includes('user denied')) ||
35089
+ ((_g = error === null || error === void 0 ? void 0 : error.message) === null || _g === void 0 ? void 0 : _g.toLowerCase().includes('user cancelled')) ||
35090
+ ((_h = error === null || error === void 0 ? void 0 : error.message) === null || _h === void 0 ? void 0 : _h.toLowerCase().includes('user canceled')) ||
35091
+ ((_j = error === null || error === void 0 ? void 0 : error.message) === null || _j === void 0 ? void 0 : _j.toLowerCase().includes('user declined'));
35092
+ if (!isUserRejection) {
35093
+ console.error('[SolanaMobileWallet] Login failed:', error);
35094
+ }
35095
+ throw error;
35096
+ }
35097
+ finally {
35098
+ setAuthLoading(false);
35099
+ }
35100
+ }
35101
+ async restoreSession() {
35102
+ const session = await WebSessionManager.getSession();
35103
+ if (session) {
35104
+ this.authorizedPublicKey = session.address;
35105
+ return { provider: this, address: session.address };
35106
+ }
35107
+ return null;
35108
+ }
35109
+ async logout() {
35110
+ var _a;
35111
+ try {
35112
+ if ((_a = this.mwaAdapter) === null || _a === void 0 ? void 0 : _a.connected) {
35113
+ await this.mwaAdapter.disconnect();
35114
+ }
35115
+ }
35116
+ catch (error) {
35117
+ console.error('[SolanaMobileWallet] Disconnect error:', error);
35118
+ }
35119
+ this.authorizedPublicKey = null;
35120
+ WebSessionManager.clearSession();
35121
+ setCurrentUser(null);
35122
+ }
35123
+ async signMessage(message) {
35124
+ var _a, _b;
35125
+ const adapter = await this.ensureAdapter();
35126
+ if (!adapter.connected) {
35127
+ await adapter.connect();
35128
+ }
35129
+ try {
35130
+ const messageBytes = new TextEncoder().encode(message);
35131
+ const signatureBytes = await adapter.signMessage(messageBytes);
35132
+ return bufferExports.Buffer.from(signatureBytes).toString('base64');
35133
+ }
35134
+ catch (error) {
35135
+ 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'))) {
35136
+ await this.logout();
35137
+ throw new Error('Wallet connection lost. Please reconnect.');
35138
+ }
35139
+ throw new Error(`Failed to sign message: ${error.message}`);
35140
+ }
35141
+ }
35142
+ async signTransaction(transaction) {
35143
+ var _a, _b;
35144
+ const adapter = await this.ensureAdapter();
35145
+ if (!adapter.connected) {
35146
+ await adapter.connect();
35147
+ }
35148
+ // Ensure blockhash is set
35149
+ const isLegacyTransaction = 'recentBlockhash' in transaction && !('message' in transaction && 'staticAccountKeys' in transaction.message);
35150
+ if (isLegacyTransaction) {
35151
+ const legacyTx = transaction;
35152
+ if (!legacyTx.recentBlockhash) {
35153
+ const rpcUrl = this.getRpcUrl();
35154
+ const connection = new web3_js.Connection(rpcUrl, 'confirmed');
35155
+ const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');
35156
+ legacyTx.recentBlockhash = blockhash;
35157
+ legacyTx.lastValidBlockHeight = lastValidBlockHeight;
35158
+ }
35159
+ if (!legacyTx.feePayer && adapter.publicKey) {
35160
+ legacyTx.feePayer = adapter.publicKey;
35161
+ }
35162
+ }
35163
+ else {
35164
+ const versionedTx = transaction;
35165
+ if (!versionedTx.message.recentBlockhash) {
35166
+ const rpcUrl = this.getRpcUrl();
35167
+ const connection = new web3_js.Connection(rpcUrl, 'confirmed');
35168
+ const { blockhash } = await connection.getLatestBlockhash('confirmed');
35169
+ versionedTx.message.recentBlockhash = blockhash;
35170
+ }
35171
+ }
35172
+ try {
35173
+ const signed = await adapter.signTransaction(transaction);
35174
+ return signed;
35175
+ }
35176
+ catch (error) {
35177
+ 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'))) {
35178
+ await this.logout();
35179
+ throw new Error('Wallet connection lost. Please reconnect.');
35180
+ }
35181
+ throw new Error(`Failed to sign transaction: ${error.message}`);
35182
+ }
35183
+ }
35184
+ async signAndSubmitTransaction(transaction, feePayer) {
35185
+ var _a, _b, _c, _d, _e, _f;
35186
+ const adapter = await this.ensureAdapter();
35187
+ if (!adapter.connected) {
35188
+ await adapter.connect();
35189
+ }
35190
+ const rpcUrl = this.getRpcUrl();
35191
+ const connection = new web3_js.Connection(rpcUrl, 'confirmed');
35192
+ const isSurfnet = rpcUrl === SURFNET_RPC_URL;
35193
+ try {
35194
+ const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');
35195
+ const isLegacyTransaction = 'recentBlockhash' in transaction && !('message' in transaction && 'staticAccountKeys' in transaction.message);
35196
+ if (isLegacyTransaction) {
35197
+ const legacyTx = transaction;
35198
+ legacyTx.recentBlockhash = blockhash;
35199
+ legacyTx.lastValidBlockHeight = lastValidBlockHeight;
35200
+ if (!legacyTx.feePayer) {
35201
+ if (feePayer) {
35202
+ legacyTx.feePayer = feePayer;
35203
+ }
35204
+ else if (adapter.publicKey) {
35205
+ legacyTx.feePayer = adapter.publicKey;
35206
+ }
35207
+ }
35208
+ }
35209
+ else {
35210
+ const versionedTx = transaction;
35211
+ versionedTx.message.recentBlockhash = blockhash;
35212
+ }
35213
+ // On surfnet, sign then submit manually
35214
+ if (isSurfnet) {
35215
+ const signedTx = await adapter.signTransaction(transaction);
35216
+ const signature = await connection.sendRawTransaction(signedTx.serialize(), {
35217
+ preflightCommitment: 'confirmed'
35218
+ });
35219
+ const confirmation = await connection.confirmTransaction({
35220
+ signature,
35221
+ blockhash,
35222
+ lastValidBlockHeight,
35223
+ }, 'confirmed');
35224
+ if (confirmation.value.err) {
35225
+ throw new Error(`Transaction failed: ${confirmation.value.err.toString()}`);
35226
+ }
35227
+ return signature;
35228
+ }
35229
+ // MWA supports signAndSendTransaction natively — this lets the wallet
35230
+ // app submit the transaction directly, which is more reliable on mobile
35231
+ if (adapter.sendTransaction) {
35232
+ const signature = await adapter.sendTransaction(transaction, connection, {
35233
+ preflightCommitment: 'confirmed',
35234
+ });
35235
+ await confirmAndCheckTransaction(connection, signature);
35236
+ return signature;
35237
+ }
35238
+ // Fallback: sign then submit
35239
+ const signedTx = await adapter.signTransaction(transaction);
35240
+ const signature = await connection.sendRawTransaction(signedTx.serialize(), {
35241
+ preflightCommitment: 'confirmed'
35242
+ });
35243
+ await confirmAndCheckTransaction(connection, signature);
35244
+ return signature;
35245
+ }
35246
+ catch (error) {
35247
+ 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'))) {
35248
+ await this.logout();
35249
+ throw new Error('Wallet connection lost. Please reconnect.');
35250
+ }
35251
+ const isUserRejection = (error === null || error === void 0 ? void 0 : error.code) === 4001 ||
35252
+ ((_c = error === null || error === void 0 ? void 0 : error.message) === null || _c === void 0 ? void 0 : _c.toLowerCase().includes('user rejected')) ||
35253
+ ((_d = error === null || error === void 0 ? void 0 : error.message) === null || _d === void 0 ? void 0 : _d.toLowerCase().includes('user denied')) ||
35254
+ ((_e = error === null || error === void 0 ? void 0 : error.message) === null || _e === void 0 ? void 0 : _e.toLowerCase().includes('user cancelled')) ||
35255
+ ((_f = error === null || error === void 0 ? void 0 : error.message) === null || _f === void 0 ? void 0 : _f.toLowerCase().includes('user canceled'));
35256
+ if (!isUserRejection) {
35257
+ console.error('[SolanaMobileWallet] Transaction failed:', error);
35258
+ }
35259
+ throw new Error(`Failed to execute transaction: ${error.message}`);
35260
+ }
35261
+ }
35262
+ async runTransaction(_evmTransactionData, solTransactionData, options) {
35263
+ var _a, _b, _c, _d, _e, _f, _g, _h;
35264
+ if (!solTransactionData) {
35265
+ throw new Error('Solana transaction data is required for mobile wallet');
35266
+ }
35267
+ const adapter = await this.ensureAdapter();
35268
+ if (!adapter.connected) {
35269
+ await this.login();
35270
+ }
35271
+ const rpcUrl = this.getRpcUrl(solTransactionData.network);
35272
+ const connection = new web3_js.Connection(rpcUrl, 'confirmed');
35273
+ const isSurfnet = rpcUrl === SURFNET_RPC_URL;
35274
+ try {
35275
+ const publicKey = adapter.publicKey;
35276
+ if (!publicKey) {
35277
+ throw new Error('No wallet connected');
35278
+ }
35279
+ const remainingAccounts = convertRemainingAccounts(solTransactionData.txArgs[0].remainingAccounts);
35280
+ let app_id = solTransactionData.appId;
35281
+ if (typeof window !== 'undefined' && window.CUSTOM_TAROBASE_APP_ID_HEADER) {
35282
+ app_id = window.CUSTOM_TAROBASE_APP_ID_HEADER;
35283
+ }
35284
+ if (!app_id) {
35285
+ throw new Error('App ID is required');
35286
+ }
35287
+ // Create wallet adapter interface for Anchor
35288
+ const walletAdapter = {
35289
+ publicKey,
35290
+ signTransaction: async (tx) => {
35291
+ return await adapter.signTransaction(tx);
35292
+ },
35293
+ signAllTransactions: async (txs) => {
35294
+ return await adapter.signAllTransactions(txs);
35295
+ }
35296
+ };
35297
+ const anchorProvider = new anchor__namespace.AnchorProvider(connection, walletAdapter, anchor__namespace.AnchorProvider.defaultOptions());
35298
+ const finalDeduped = [];
35299
+ for (const acc of remainingAccounts) {
35300
+ const existing = finalDeduped.find((d) => d.pubkey.equals(acc.pubkey));
35301
+ if (existing) {
35302
+ existing.isSigner = existing.isSigner || acc.isSigner;
35303
+ existing.isWritable = existing.isWritable || acc.isWritable;
35304
+ }
35305
+ else {
35306
+ finalDeduped.push(acc);
35307
+ }
35308
+ }
35309
+ const { tx } = await buildSetDocumentsTransaction(connection, solTransactionData.txArgs[0].idl, anchorProvider, publicKey, {
35310
+ app_id,
35311
+ documents: solTransactionData.txArgs[0].setDocumentData,
35312
+ delete_paths: solTransactionData.txArgs[0].deletePaths,
35313
+ txData: solTransactionData.txArgs[0].txData
35314
+ }, finalDeduped, solTransactionData.lutKey, solTransactionData.preInstructions, false);
35315
+ if ((options === null || options === void 0 ? void 0 : options.shouldSubmitTx) === false) {
35316
+ const signedTx = await walletAdapter.signTransaction(tx);
35317
+ return {
35318
+ signedTransaction: signedTx,
35319
+ blockNumber: 0,
35320
+ gasUsed: '0',
35321
+ data: ''
35322
+ };
35323
+ }
35324
+ // On surfnet, sign + submit manually
35325
+ if (isSurfnet) {
35326
+ const signedTx = await walletAdapter.signTransaction(tx);
35327
+ const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');
35328
+ const signature = await connection.sendRawTransaction(signedTx.serialize(), {
35329
+ preflightCommitment: 'confirmed'
35330
+ });
35331
+ const confirmation = await connection.confirmTransaction({
35332
+ signature,
35333
+ blockhash,
35334
+ lastValidBlockHeight,
35335
+ }, 'confirmed');
35336
+ if (confirmation.value.err) {
35337
+ throw new Error(`Transaction failed: ${confirmation.value.err.toString()}`);
35338
+ }
35339
+ const txInfo = await connection.getParsedTransaction(signature, {
35340
+ maxSupportedTransactionVersion: 0,
35341
+ commitment: 'confirmed'
35342
+ });
35343
+ return {
35344
+ transactionSignature: signature,
35345
+ blockNumber: (txInfo === null || txInfo === void 0 ? void 0 : txInfo.slot) || 0,
35346
+ gasUsed: ((_a = txInfo === null || txInfo === void 0 ? void 0 : txInfo.meta) === null || _a === void 0 ? void 0 : _a.fee.toString()) || '0',
35347
+ data: txInfo === null || txInfo === void 0 ? void 0 : txInfo.meta,
35348
+ };
35349
+ }
35350
+ // Use MWA's sendTransaction for mobile-optimized submission
35351
+ let signature;
35352
+ if (adapter.sendTransaction) {
35353
+ signature = await adapter.sendTransaction(tx, connection, {
35354
+ preflightCommitment: 'confirmed',
35355
+ });
35356
+ }
35357
+ else {
35358
+ const signedTx = await walletAdapter.signTransaction(tx);
35359
+ signature = await connection.sendRawTransaction(signedTx.serialize(), {
35360
+ preflightCommitment: 'confirmed'
35361
+ });
35362
+ }
35363
+ const txInfo = await confirmAndCheckTransaction(connection, signature);
35364
+ return {
35365
+ transactionSignature: signature,
35366
+ blockNumber: (txInfo === null || txInfo === void 0 ? void 0 : txInfo.slot) || 0,
35367
+ gasUsed: ((_b = txInfo === null || txInfo === void 0 ? void 0 : txInfo.meta) === null || _b === void 0 ? void 0 : _b.fee.toString()) || '0',
35368
+ data: txInfo === null || txInfo === void 0 ? void 0 : txInfo.meta,
35369
+ };
35370
+ }
35371
+ catch (error) {
35372
+ if (((_c = error === null || error === void 0 ? void 0 : error.message) === null || _c === void 0 ? void 0 : _c.includes('not connected')) || ((_d = error === null || error === void 0 ? void 0 : error.message) === null || _d === void 0 ? void 0 : _d.includes('not authorized'))) {
35373
+ await this.logout();
35374
+ throw new Error('Wallet connection lost. Please reconnect.');
35375
+ }
35376
+ const isUserRejection = (error === null || error === void 0 ? void 0 : error.code) === 4001 ||
35377
+ ((_e = error === null || error === void 0 ? void 0 : error.message) === null || _e === void 0 ? void 0 : _e.toLowerCase().includes('user rejected')) ||
35378
+ ((_f = error === null || error === void 0 ? void 0 : error.message) === null || _f === void 0 ? void 0 : _f.toLowerCase().includes('user denied')) ||
35379
+ ((_g = error === null || error === void 0 ? void 0 : error.message) === null || _g === void 0 ? void 0 : _g.toLowerCase().includes('user cancelled')) ||
35380
+ ((_h = error === null || error === void 0 ? void 0 : error.message) === null || _h === void 0 ? void 0 : _h.toLowerCase().includes('user canceled'));
35381
+ if (!isUserRejection) {
35382
+ console.error('[SolanaMobileWallet] Transaction failed:', error);
35383
+ }
35384
+ throw new Error(`Failed to execute transaction: ${error.message}`);
35385
+ }
35386
+ }
35387
+ async getNativeMethods() {
35388
+ return this.mwaAdapter;
35389
+ }
35390
+ /* ----------------------------------------------------------- *
35391
+ * Private Helpers
35392
+ * ----------------------------------------------------------- */
35393
+ getRpcUrl(network) {
35394
+ if (this.networkUrl) {
35395
+ return this.networkUrl;
35396
+ }
35397
+ if (network === 'solana_devnet') {
35398
+ return SOLANA_DEVNET_RPC_URL;
35399
+ }
35400
+ else if (network === 'solana_mainnet') {
35401
+ return SOLANA_MAINNET_RPC_URL;
35402
+ }
35403
+ else if (network === 'surfnet') {
35404
+ return SURFNET_RPC_URL;
35405
+ }
35406
+ return SOLANA_MAINNET_RPC_URL;
35407
+ }
35408
+ }
35409
+ SolanaMobileWalletProvider.instance = null;
35410
+
34927
35411
  let currentAuthProvider = null;
34928
35412
  let currentAuthMethod = null;
34929
35413
  let initConfig = null;
@@ -34997,7 +35481,7 @@ const SOLANA_DEVNET_RPC_URL = "https://idelle-8nxsep-fast-devnet.helius-rpc.com"
34997
35481
  const SOLANA_MAINNET_RPC_URL = "https://celestia-cegncv-fast-mainnet.helius-rpc.com";
34998
35482
  const SURFNET_RPC_URL = "https://surfpool.fly.dev";
34999
35483
  async function getAuthProvider(config) {
35000
- var _a, _b, _c, _d;
35484
+ var _a, _b, _c, _d, _e, _f;
35001
35485
  if (currentAuthProvider) {
35002
35486
  return currentAuthProvider;
35003
35487
  }
@@ -35012,7 +35496,7 @@ async function getAuthProvider(config) {
35012
35496
  return currentAuthProvider;
35013
35497
  }
35014
35498
  // If the user previously logged in with a specific method, use that instead
35015
- const validAuthMethods = ['privy', 'phantom', 'wallet', 'rainbowkit', 'coinbase-smart-wallet', 'onboard', 'none'];
35499
+ const validAuthMethods = ['privy', 'phantom', 'wallet', 'rainbowkit', 'coinbase-smart-wallet', 'onboard', 'mobile-wallet-adapter', 'none'];
35016
35500
  const storedMethod = getStoredAuthMethod();
35017
35501
  const authMethod = (storedMethod && validAuthMethods.includes(storedMethod))
35018
35502
  ? storedMethod
@@ -35021,7 +35505,25 @@ async function getAuthProvider(config) {
35021
35505
  // Invalid stored value — clear it
35022
35506
  setStoredAuthMethod(null);
35023
35507
  }
35024
- const rpcUrl = (_a = config.rpcUrl) !== null && _a !== void 0 ? _a : null;
35508
+ // --- Register MWA as a wallet-standard wallet on mobile ---
35509
+ // On Android (TWA via Bubblewrap, Chrome, or mobile browser), register the
35510
+ // Mobile Wallet Adapter so it appears in wallet discovery (e.g. Phantom's
35511
+ // useDiscoveredWallets, or any wallet-standard consumer). This makes Seed
35512
+ // Vault, Solflare, and other MWA wallets show up alongside injected wallets
35513
+ // in the existing Phantom/Privy modals — without overriding the configured
35514
+ // authMethod. Email login, deeplinks, and all other flows stay intact.
35515
+ //
35516
+ // Only authMethod: 'mobile-wallet-adapter' (explicit opt-in) bypasses
35517
+ // Phantom/Privy entirely and uses MWA as the sole provider.
35518
+ if (isMobileWalletAvailable()) {
35519
+ registerMobileWalletAdapter({
35520
+ appIdentity: (_b = (_a = config.mobileWalletConfig) === null || _a === void 0 ? void 0 : _a.appIdentity) !== null && _b !== void 0 ? _b : {
35521
+ name: config.name || undefined,
35522
+ uri: typeof window !== 'undefined' ? window.location.origin : undefined,
35523
+ },
35524
+ }).catch(() => { });
35525
+ }
35526
+ const rpcUrl = (_c = config.rpcUrl) !== null && _c !== void 0 ? _c : null;
35025
35527
  currentAuthMethod = authMethod;
35026
35528
  switch (authMethod) {
35027
35529
  case "wallet":
@@ -35031,15 +35533,18 @@ async function getAuthProvider(config) {
35031
35533
  console.warn("Rainbow Kit auth is not yet supported.");
35032
35534
  break;
35033
35535
  case "privy":
35034
- currentAuthProvider = new PrivyWalletProvider((_b = config.name) !== null && _b !== void 0 ? _b : null, (_c = config.logoUrl) !== null && _c !== void 0 ? _c : null, config.privyConfig, rpcUrl);
35536
+ currentAuthProvider = new PrivyWalletProvider((_d = config.name) !== null && _d !== void 0 ? _d : null, (_e = config.logoUrl) !== null && _e !== void 0 ? _e : null, config.privyConfig, rpcUrl);
35035
35537
  break;
35036
35538
  case "phantom":
35037
35539
  currentAuthProvider = new PhantomWalletProvider(rpcUrl, config.phantomConfig);
35038
- if ((_d = config.phantomConfig) === null || _d === void 0 ? void 0 : _d.enablePrivyFallback) {
35540
+ if ((_f = config.phantomConfig) === null || _f === void 0 ? void 0 : _f.enablePrivyFallback) {
35039
35541
  currentAuthProvider.onSwitchToPrivy =
35040
35542
  () => hotSwapToPrivyProvider(config);
35041
35543
  }
35042
35544
  break;
35545
+ case "mobile-wallet-adapter":
35546
+ currentAuthProvider = new SolanaMobileWalletProvider(rpcUrl, config.mobileWalletConfig);
35547
+ break;
35043
35548
  case "onboard":
35044
35549
  console.warn("Onboard auth is not yet supported.");
35045
35550
  break;
@@ -35262,6 +35767,7 @@ exports.OffchainAuthProvider = OffchainAuthProvider;
35262
35767
  exports.PhantomWalletProvider = PhantomWalletProvider;
35263
35768
  exports.PrivyWalletProvider = PrivyWalletProvider;
35264
35769
  exports.ServerSessionManager = ServerSessionManager;
35770
+ exports.SolanaMobileWalletProvider = SolanaMobileWalletProvider;
35265
35771
  exports.WebSessionManager = WebSessionManager;
35266
35772
  exports.aggregate = aggregate;
35267
35773
  exports.bs58 = bs58;
@@ -35284,12 +35790,14 @@ exports.getCurrentUser = getCurrentUser;
35284
35790
  exports.getFiles = getFiles;
35285
35791
  exports.getIdToken = getIdToken;
35286
35792
  exports.init = init;
35793
+ exports.isMobileWalletAvailable = isMobileWalletAvailable;
35287
35794
  exports.login = login;
35288
35795
  exports.logout = logout;
35289
35796
  exports.onAuthLoadingChanged = onAuthLoadingChanged;
35290
35797
  exports.onAuthStateChanged = onAuthStateChanged;
35291
35798
  exports.reconnectWithNewAuth = reconnectWithNewAuth;
35292
35799
  exports.refreshSession = refreshSession;
35800
+ exports.registerMobileWalletAdapter = registerMobileWalletAdapter;
35293
35801
  exports.runExpression = runExpression;
35294
35802
  exports.runExpressionMany = runExpressionMany;
35295
35803
  exports.runQuery = runQuery;
@@ -35303,4 +35811,4 @@ exports.signSessionCreateMessage = signSessionCreateMessage;
35303
35811
  exports.signTransaction = signTransaction;
35304
35812
  exports.subscribe = subscribe;
35305
35813
  exports.useAuth = useAuth;
35306
- //# sourceMappingURL=index-D4MRp3aP.js.map
35814
+ //# sourceMappingURL=index-C4TkISTV.js.map