@pooflabs/web 0.0.69 → 0.0.70-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.
@@ -10777,7 +10777,10 @@ function handleServerMessage(connection, message) {
10777
10777
  }
10778
10778
  function notifyCallbacks(subscription, data) {
10779
10779
  var _a;
10780
- 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) {
10781
10784
  try {
10782
10785
  (_a = callback.onData) === null || _a === void 0 ? void 0 : _a.call(callback, data);
10783
10786
  }
@@ -13563,7 +13566,7 @@ async function loadDependencies() {
13563
13566
  const [reactModule, reactDomModule, phantomModule] = await Promise.all([
13564
13567
  import('react'),
13565
13568
  import('react-dom/client'),
13566
- Promise.resolve().then(function () { return require('./index-DG-p1jRT.js'); })
13569
+ Promise.resolve().then(function () { return require('./index-BO2G95MR.js'); })
13567
13570
  ]);
13568
13571
  // Extract default export from ESM module namespace
13569
13572
  // Dynamic import() returns { default: Module, ...exports }, not the module directly
@@ -13586,6 +13589,8 @@ class PhantomWalletProvider {
13586
13589
  this.initPromise = null;
13587
13590
  /** Callback to swap to a Privy provider when the user clicks "Log in with email" */
13588
13591
  this.onSwitchToPrivy = null;
13592
+ /** Callback to swap to a Mobile Wallet Adapter provider when the user clicks "Connect Mobile Wallet" */
13593
+ this.onSwitchToMWA = null;
13589
13594
  this.networkUrl = networkUrl;
13590
13595
  this.config = config;
13591
13596
  if (typeof window === 'undefined') {
@@ -13953,6 +13958,31 @@ class PhantomWalletProvider {
13953
13958
  }
13954
13959
  }
13955
13960
  };
13961
+ const handleMobileWalletClick = async () => {
13962
+ that.loginInProgress = false;
13963
+ walletClickedRef.current = true;
13964
+ setShowWalletModal(false);
13965
+ if (that.onSwitchToMWA) {
13966
+ try {
13967
+ const mwaProvider = await that.onSwitchToMWA();
13968
+ const user = await mwaProvider.login();
13969
+ if (that.pendingLogin && user) {
13970
+ that.pendingLogin.resolve(user);
13971
+ that.pendingLogin = null;
13972
+ }
13973
+ else if (that.pendingLogin) {
13974
+ that.pendingLogin.reject(new Error('User cancelled login'));
13975
+ that.pendingLogin = null;
13976
+ }
13977
+ }
13978
+ catch (error) {
13979
+ if (that.pendingLogin) {
13980
+ that.pendingLogin.reject(error);
13981
+ that.pendingLogin = null;
13982
+ }
13983
+ }
13984
+ }
13985
+ };
13956
13986
  const handleCloseModal = () => {
13957
13987
  setShowWalletModal(false);
13958
13988
  if (that.loginInProgress && that.pendingLogin && !(phantom === null || phantom === void 0 ? void 0 : phantom.isConnected)) {
@@ -14028,6 +14058,37 @@ class PhantomWalletProvider {
14028
14058
  style: { width: '28px', height: '28px', borderRadius: '6px' },
14029
14059
  }), 'Open Phantom app'));
14030
14060
  }
14061
+ // Mobile Wallet Adapter button — shown on Android when MWA callback is available
14062
+ const isAndroid = /Android/i.test(navigator.userAgent);
14063
+ if (isAndroid && that.onSwitchToMWA) {
14064
+ walletButtons.push(React$1.createElement('button', {
14065
+ key: 'mobile-wallet',
14066
+ style: buttonStyle('mobile-wallet'),
14067
+ onClick: handleMobileWalletClick,
14068
+ onMouseEnter: () => setHoveredBtn('mobile-wallet'),
14069
+ onMouseLeave: () => setHoveredBtn(null),
14070
+ },
14071
+ // Mobile wallet icon (phone with wallet)
14072
+ React$1.createElement('svg', {
14073
+ width: '20', height: '20', viewBox: '0 0 24 24', fill: 'none',
14074
+ style: { flexShrink: '0' },
14075
+ },
14076
+ // Phone outline
14077
+ React$1.createElement('rect', {
14078
+ x: '5', y: '2', width: '14', height: '20', rx: '2',
14079
+ stroke: textColor, strokeWidth: '2', fill: 'none',
14080
+ }),
14081
+ // Screen line
14082
+ React$1.createElement('line', {
14083
+ x1: '5', y1: '6', x2: '19', y2: '6',
14084
+ stroke: textColor, strokeWidth: '1.5',
14085
+ }),
14086
+ // Wallet/shield checkmark
14087
+ React$1.createElement('path', {
14088
+ d: 'M9.5 12.5l2 2 3.5-3.5',
14089
+ stroke: textColor, strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round', fill: 'none',
14090
+ })), 'Connect Mobile Wallet'));
14091
+ }
14031
14092
  // Email button (always shown in custom modal since enablePrivyFallback is true)
14032
14093
  walletButtons.push(React$1.createElement('button', {
14033
14094
  key: 'email-login',
@@ -34903,6 +34964,520 @@ class OffchainAuthProvider {
34903
34964
  }
34904
34965
  }
34905
34966
 
34967
+ /**
34968
+ * Detects whether the current environment is a mobile browser capable of
34969
+ * Mobile Wallet Adapter (MWA) communication.
34970
+ *
34971
+ * Returns true on Android browsers (Chrome, etc.) where MWA intents work,
34972
+ * or inside a Seeker/Saga in-app browser context.
34973
+ */
34974
+ function isMobileWalletAvailable() {
34975
+ if (typeof window === 'undefined' || typeof navigator === 'undefined')
34976
+ return false;
34977
+ const ua = navigator.userAgent || '';
34978
+ // Android browser — MWA uses Android intents
34979
+ const isAndroid = /Android/i.test(ua);
34980
+ // In-app browser inside a wallet app on Seeker/Saga
34981
+ const isWalletInAppBrowser = /SolanaWallet/i.test(ua) || /SeedVault/i.test(ua);
34982
+ return isAndroid || isWalletInAppBrowser;
34983
+ }
34984
+ // Dynamically imported MWA module
34985
+ let mwaModule = null;
34986
+ let mwaLoadPromise = null;
34987
+ async function loadMwaModule() {
34988
+ if (mwaModule)
34989
+ return;
34990
+ if (typeof window === 'undefined')
34991
+ return;
34992
+ if (mwaLoadPromise)
34993
+ return mwaLoadPromise;
34994
+ mwaLoadPromise = (async () => {
34995
+ try {
34996
+ mwaModule = await import('@solana-mobile/wallet-adapter-mobile');
34997
+ }
34998
+ catch (e) {
34999
+ console.warn('[SolanaMobileWallet] @solana-mobile/wallet-adapter-mobile not installed. Install it to enable Seeker wallet support.');
35000
+ throw new Error('Missing @solana-mobile/wallet-adapter-mobile dependency');
35001
+ }
35002
+ })();
35003
+ return mwaLoadPromise;
35004
+ }
35005
+ /**
35006
+ * Registers Mobile Wallet Adapter as a wallet-standard wallet so it appears
35007
+ * in wallet selection UIs and can be discovered by other wallet-standard consumers.
35008
+ *
35009
+ * Call this once at app startup (in a browser-only / non-SSR context).
35010
+ *
35011
+ * @param config - App identity and optional remote host authority for desktop QR code support
35012
+ */
35013
+ async function registerMobileWalletAdapter(config) {
35014
+ var _a;
35015
+ if (typeof window === 'undefined')
35016
+ return;
35017
+ try {
35018
+ const walletStandardMobile = await import('@solana-mobile/wallet-standard-mobile');
35019
+ const registerMwa = walletStandardMobile.registerMwa || ((_a = walletStandardMobile.default) === null || _a === void 0 ? void 0 : _a.registerMwa);
35020
+ if (!registerMwa) {
35021
+ console.warn('[SolanaMobileWallet] registerMwa not found in @solana-mobile/wallet-standard-mobile');
35022
+ return;
35023
+ }
35024
+ const options = {};
35025
+ if (config === null || config === void 0 ? void 0 : config.appIdentity) {
35026
+ options.appIdentity = config.appIdentity;
35027
+ }
35028
+ if (config === null || config === void 0 ? void 0 : config.chains) {
35029
+ options.chains = config.chains;
35030
+ }
35031
+ if (config === null || config === void 0 ? void 0 : config.remoteHostAuthority) {
35032
+ options.remoteHostAuthority = config.remoteHostAuthority;
35033
+ }
35034
+ // Use the library's default helpers if available
35035
+ if (walletStandardMobile.createDefaultAuthorizationCache) {
35036
+ options.authorizationCache = walletStandardMobile.createDefaultAuthorizationCache();
35037
+ }
35038
+ if (walletStandardMobile.createDefaultChainSelector) {
35039
+ options.chainSelector = walletStandardMobile.createDefaultChainSelector();
35040
+ }
35041
+ if (walletStandardMobile.createDefaultWalletNotFoundHandler) {
35042
+ options.onWalletNotFound = walletStandardMobile.createDefaultWalletNotFoundHandler();
35043
+ }
35044
+ registerMwa(options);
35045
+ }
35046
+ catch (e) {
35047
+ // @solana-mobile/wallet-standard-mobile is an optional dependency
35048
+ // Silently skip if not installed — the provider still works via
35049
+ // @solana-mobile/wallet-adapter-mobile directly
35050
+ console.debug('[SolanaMobileWallet] @solana-mobile/wallet-standard-mobile not available, skipping wallet-standard registration');
35051
+ }
35052
+ }
35053
+ /**
35054
+ * SolanaMobileWalletProvider implements the AuthProvider interface using the
35055
+ * Solana Mobile Wallet Adapter (MWA) protocol.
35056
+ *
35057
+ * This enables TaroBase dApps to work with any MWA-compliant wallet on Solana
35058
+ * mobile devices (Seeker, Saga) — including the native Seed Vault Wallet,
35059
+ * Phantom, Solflare, and any other wallet that implements the MWA protocol.
35060
+ *
35061
+ * The MWA protocol communicates with wallet apps via Android intents (on mobile)
35062
+ * or WebSocket relay (on desktop via QR code), eliminating the need to integrate
35063
+ * with each wallet individually.
35064
+ */
35065
+ class SolanaMobileWalletProvider {
35066
+ constructor(networkUrl = null, config = {}) {
35067
+ this.mwaAdapter = null;
35068
+ this.authorizedPublicKey = null;
35069
+ this.networkUrl = networkUrl;
35070
+ this.config = config;
35071
+ if (typeof window === 'undefined') {
35072
+ throw new Error('SolanaMobileWalletProvider can only be instantiated in a browser environment');
35073
+ }
35074
+ if (SolanaMobileWalletProvider.instance) {
35075
+ return SolanaMobileWalletProvider.instance;
35076
+ }
35077
+ SolanaMobileWalletProvider.instance = this;
35078
+ }
35079
+ static getInstance(networkUrl, config) {
35080
+ if (!SolanaMobileWalletProvider.instance) {
35081
+ new SolanaMobileWalletProvider(networkUrl, config);
35082
+ }
35083
+ return SolanaMobileWalletProvider.instance;
35084
+ }
35085
+ async ensureAdapter() {
35086
+ var _a;
35087
+ if ((_a = this.mwaAdapter) === null || _a === void 0 ? void 0 : _a.connected)
35088
+ return this.mwaAdapter;
35089
+ await loadMwaModule();
35090
+ const { SolanaMobileWalletAdapter, createDefaultAddressSelector, createDefaultAuthorizationResultCache } = mwaModule;
35091
+ if (!this.mwaAdapter) {
35092
+ const cluster = this.config.cluster || 'mainnet-beta';
35093
+ this.mwaAdapter = new SolanaMobileWalletAdapter({
35094
+ addressSelector: createDefaultAddressSelector(),
35095
+ appIdentity: this.config.appIdentity || {
35096
+ name: 'TaroBase App',
35097
+ uri: typeof window !== 'undefined' ? window.location.origin : undefined,
35098
+ },
35099
+ authorizationResultCache: createDefaultAuthorizationResultCache(),
35100
+ cluster,
35101
+ });
35102
+ }
35103
+ return this.mwaAdapter;
35104
+ }
35105
+ async login() {
35106
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
35107
+ setAuthLoading(true);
35108
+ try {
35109
+ const adapter = await this.ensureAdapter();
35110
+ // Connect triggers the MWA authorization flow — this opens the wallet app
35111
+ // via Android intent and asks the user to authorize this dApp
35112
+ await adapter.connect();
35113
+ // The MWA adapter populates `publicKey` asynchronously via an internal
35114
+ // change-event handler that fires after connect() resolves. Poll for it
35115
+ // with a short timeout to handle this race condition.
35116
+ let publicKey;
35117
+ const maxWaitMs = 3000;
35118
+ const pollIntervalMs = 50;
35119
+ const start = Date.now();
35120
+ while (Date.now() - start < maxWaitMs) {
35121
+ 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));
35122
+ if (publicKey)
35123
+ break;
35124
+ await new Promise(r => setTimeout(r, pollIntervalMs));
35125
+ }
35126
+ if (!publicKey) {
35127
+ throw new Error('No public key returned from wallet');
35128
+ }
35129
+ this.authorizedPublicKey = publicKey;
35130
+ // Check if we already have a valid session
35131
+ const existingSession = await WebSessionManager.getSession();
35132
+ if (existingSession && existingSession.address === publicKey) {
35133
+ const user = { provider: this, address: publicKey };
35134
+ setCurrentUser(user);
35135
+ return user;
35136
+ }
35137
+ // Create new session with signature
35138
+ const nonce = await genAuthNonce();
35139
+ const messageText = await genSolanaMessage(publicKey, nonce);
35140
+ // MWA signMessage expects Uint8Array
35141
+ const messageBytes = new TextEncoder().encode(messageText);
35142
+ const signatureBytes = await adapter.signMessage(messageBytes);
35143
+ const signature = bufferExports.Buffer.from(signatureBytes).toString('base64');
35144
+ const createSessionResult = await createSessionWithSignature(publicKey, messageText, signature);
35145
+ await WebSessionManager.storeSession(publicKey, createSessionResult.accessToken, createSessionResult.idToken, createSessionResult.refreshToken);
35146
+ // Mark auth method
35147
+ try {
35148
+ localStorage.setItem('tarobase_last_auth_method', 'mobile-wallet-adapter');
35149
+ }
35150
+ catch (_k) { }
35151
+ const user = { provider: this, address: publicKey };
35152
+ setCurrentUser(user);
35153
+ return user;
35154
+ }
35155
+ catch (error) {
35156
+ const isUserRejection = (error === null || error === void 0 ? void 0 : error.code) === 4001 ||
35157
+ ((_e = error === null || error === void 0 ? void 0 : error.message) === null || _e === void 0 ? void 0 : _e.toLowerCase().includes('user rejected')) ||
35158
+ ((_f = error === null || error === void 0 ? void 0 : error.message) === null || _f === void 0 ? void 0 : _f.toLowerCase().includes('user denied')) ||
35159
+ ((_g = error === null || error === void 0 ? void 0 : error.message) === null || _g === void 0 ? void 0 : _g.toLowerCase().includes('user cancelled')) ||
35160
+ ((_h = error === null || error === void 0 ? void 0 : error.message) === null || _h === void 0 ? void 0 : _h.toLowerCase().includes('user canceled')) ||
35161
+ ((_j = error === null || error === void 0 ? void 0 : error.message) === null || _j === void 0 ? void 0 : _j.toLowerCase().includes('user declined'));
35162
+ if (!isUserRejection) {
35163
+ console.error('[SolanaMobileWallet] Login failed:', error);
35164
+ }
35165
+ throw error;
35166
+ }
35167
+ finally {
35168
+ setAuthLoading(false);
35169
+ }
35170
+ }
35171
+ async restoreSession() {
35172
+ const session = await WebSessionManager.getSession();
35173
+ if (session) {
35174
+ this.authorizedPublicKey = session.address;
35175
+ return { provider: this, address: session.address };
35176
+ }
35177
+ return null;
35178
+ }
35179
+ async logout() {
35180
+ var _a;
35181
+ try {
35182
+ if ((_a = this.mwaAdapter) === null || _a === void 0 ? void 0 : _a.connected) {
35183
+ await this.mwaAdapter.disconnect();
35184
+ }
35185
+ }
35186
+ catch (error) {
35187
+ console.error('[SolanaMobileWallet] Disconnect error:', error);
35188
+ }
35189
+ this.authorizedPublicKey = null;
35190
+ WebSessionManager.clearSession();
35191
+ setCurrentUser(null);
35192
+ }
35193
+ async signMessage(message) {
35194
+ var _a, _b;
35195
+ const adapter = await this.ensureAdapter();
35196
+ if (!adapter.connected) {
35197
+ await adapter.connect();
35198
+ }
35199
+ try {
35200
+ const messageBytes = new TextEncoder().encode(message);
35201
+ const signatureBytes = await adapter.signMessage(messageBytes);
35202
+ return bufferExports.Buffer.from(signatureBytes).toString('base64');
35203
+ }
35204
+ catch (error) {
35205
+ 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'))) {
35206
+ await this.logout();
35207
+ throw new Error('Wallet connection lost. Please reconnect.');
35208
+ }
35209
+ throw new Error(`Failed to sign message: ${error.message}`);
35210
+ }
35211
+ }
35212
+ async signTransaction(transaction) {
35213
+ var _a, _b;
35214
+ const adapter = await this.ensureAdapter();
35215
+ if (!adapter.connected) {
35216
+ await adapter.connect();
35217
+ }
35218
+ // Ensure blockhash is set
35219
+ const isLegacyTransaction = 'recentBlockhash' in transaction && !('message' in transaction && 'staticAccountKeys' in transaction.message);
35220
+ if (isLegacyTransaction) {
35221
+ const legacyTx = transaction;
35222
+ if (!legacyTx.recentBlockhash) {
35223
+ const rpcUrl = this.getRpcUrl();
35224
+ const connection = new web3_js.Connection(rpcUrl, 'confirmed');
35225
+ const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');
35226
+ legacyTx.recentBlockhash = blockhash;
35227
+ legacyTx.lastValidBlockHeight = lastValidBlockHeight;
35228
+ }
35229
+ if (!legacyTx.feePayer && adapter.publicKey) {
35230
+ legacyTx.feePayer = adapter.publicKey;
35231
+ }
35232
+ }
35233
+ else {
35234
+ const versionedTx = transaction;
35235
+ if (!versionedTx.message.recentBlockhash) {
35236
+ const rpcUrl = this.getRpcUrl();
35237
+ const connection = new web3_js.Connection(rpcUrl, 'confirmed');
35238
+ const { blockhash } = await connection.getLatestBlockhash('confirmed');
35239
+ versionedTx.message.recentBlockhash = blockhash;
35240
+ }
35241
+ }
35242
+ try {
35243
+ const signed = await adapter.signTransaction(transaction);
35244
+ return signed;
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
+ throw new Error(`Failed to sign transaction: ${error.message}`);
35252
+ }
35253
+ }
35254
+ async signAndSubmitTransaction(transaction, feePayer) {
35255
+ var _a, _b, _c, _d, _e, _f;
35256
+ const adapter = await this.ensureAdapter();
35257
+ if (!adapter.connected) {
35258
+ await adapter.connect();
35259
+ }
35260
+ const rpcUrl = this.getRpcUrl();
35261
+ const connection = new web3_js.Connection(rpcUrl, 'confirmed');
35262
+ const isSurfnet = rpcUrl === SURFNET_RPC_URL;
35263
+ try {
35264
+ const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');
35265
+ const isLegacyTransaction = 'recentBlockhash' in transaction && !('message' in transaction && 'staticAccountKeys' in transaction.message);
35266
+ if (isLegacyTransaction) {
35267
+ const legacyTx = transaction;
35268
+ legacyTx.recentBlockhash = blockhash;
35269
+ legacyTx.lastValidBlockHeight = lastValidBlockHeight;
35270
+ if (!legacyTx.feePayer) {
35271
+ if (feePayer) {
35272
+ legacyTx.feePayer = feePayer;
35273
+ }
35274
+ else if (adapter.publicKey) {
35275
+ legacyTx.feePayer = adapter.publicKey;
35276
+ }
35277
+ }
35278
+ }
35279
+ else {
35280
+ const versionedTx = transaction;
35281
+ versionedTx.message.recentBlockhash = blockhash;
35282
+ }
35283
+ // On surfnet, sign then submit manually
35284
+ if (isSurfnet) {
35285
+ const signedTx = await adapter.signTransaction(transaction);
35286
+ const signature = await connection.sendRawTransaction(signedTx.serialize(), {
35287
+ preflightCommitment: 'confirmed'
35288
+ });
35289
+ const confirmation = await connection.confirmTransaction({
35290
+ signature,
35291
+ blockhash,
35292
+ lastValidBlockHeight,
35293
+ }, 'confirmed');
35294
+ if (confirmation.value.err) {
35295
+ throw new Error(`Transaction failed: ${confirmation.value.err.toString()}`);
35296
+ }
35297
+ return signature;
35298
+ }
35299
+ // MWA supports signAndSendTransaction natively — this lets the wallet
35300
+ // app submit the transaction directly, which is more reliable on mobile
35301
+ if (adapter.sendTransaction) {
35302
+ const signature = await adapter.sendTransaction(transaction, connection, {
35303
+ preflightCommitment: 'confirmed',
35304
+ });
35305
+ await confirmAndCheckTransaction(connection, signature);
35306
+ return signature;
35307
+ }
35308
+ // Fallback: sign then submit
35309
+ const signedTx = await adapter.signTransaction(transaction);
35310
+ const signature = await connection.sendRawTransaction(signedTx.serialize(), {
35311
+ preflightCommitment: 'confirmed'
35312
+ });
35313
+ await confirmAndCheckTransaction(connection, signature);
35314
+ return signature;
35315
+ }
35316
+ catch (error) {
35317
+ 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'))) {
35318
+ await this.logout();
35319
+ throw new Error('Wallet connection lost. Please reconnect.');
35320
+ }
35321
+ const isUserRejection = (error === null || error === void 0 ? void 0 : error.code) === 4001 ||
35322
+ ((_c = error === null || error === void 0 ? void 0 : error.message) === null || _c === void 0 ? void 0 : _c.toLowerCase().includes('user rejected')) ||
35323
+ ((_d = error === null || error === void 0 ? void 0 : error.message) === null || _d === void 0 ? void 0 : _d.toLowerCase().includes('user denied')) ||
35324
+ ((_e = error === null || error === void 0 ? void 0 : error.message) === null || _e === void 0 ? void 0 : _e.toLowerCase().includes('user cancelled')) ||
35325
+ ((_f = error === null || error === void 0 ? void 0 : error.message) === null || _f === void 0 ? void 0 : _f.toLowerCase().includes('user canceled'));
35326
+ if (!isUserRejection) {
35327
+ console.error('[SolanaMobileWallet] Transaction failed:', error);
35328
+ }
35329
+ throw new Error(`Failed to execute transaction: ${error.message}`);
35330
+ }
35331
+ }
35332
+ async runTransaction(_evmTransactionData, solTransactionData, options) {
35333
+ var _a, _b, _c, _d, _e, _f, _g, _h;
35334
+ if (!solTransactionData) {
35335
+ throw new Error('Solana transaction data is required for mobile wallet');
35336
+ }
35337
+ const adapter = await this.ensureAdapter();
35338
+ if (!adapter.connected) {
35339
+ await this.login();
35340
+ }
35341
+ const rpcUrl = this.getRpcUrl(solTransactionData.network);
35342
+ const connection = new web3_js.Connection(rpcUrl, 'confirmed');
35343
+ const isSurfnet = rpcUrl === SURFNET_RPC_URL;
35344
+ try {
35345
+ const publicKey = adapter.publicKey;
35346
+ if (!publicKey) {
35347
+ throw new Error('No wallet connected');
35348
+ }
35349
+ const remainingAccounts = convertRemainingAccounts(solTransactionData.txArgs[0].remainingAccounts);
35350
+ let app_id = solTransactionData.appId;
35351
+ if (typeof window !== 'undefined' && window.CUSTOM_TAROBASE_APP_ID_HEADER) {
35352
+ app_id = window.CUSTOM_TAROBASE_APP_ID_HEADER;
35353
+ }
35354
+ if (!app_id) {
35355
+ throw new Error('App ID is required');
35356
+ }
35357
+ // Create wallet adapter interface for Anchor
35358
+ const walletAdapter = {
35359
+ publicKey,
35360
+ signTransaction: async (tx) => {
35361
+ return await adapter.signTransaction(tx);
35362
+ },
35363
+ signAllTransactions: async (txs) => {
35364
+ return await adapter.signAllTransactions(txs);
35365
+ }
35366
+ };
35367
+ const anchorProvider = new anchor__namespace.AnchorProvider(connection, walletAdapter, anchor__namespace.AnchorProvider.defaultOptions());
35368
+ const finalDeduped = [];
35369
+ for (const acc of remainingAccounts) {
35370
+ const existing = finalDeduped.find((d) => d.pubkey.equals(acc.pubkey));
35371
+ if (existing) {
35372
+ existing.isSigner = existing.isSigner || acc.isSigner;
35373
+ existing.isWritable = existing.isWritable || acc.isWritable;
35374
+ }
35375
+ else {
35376
+ finalDeduped.push(acc);
35377
+ }
35378
+ }
35379
+ const { tx } = await buildSetDocumentsTransaction(connection, solTransactionData.txArgs[0].idl, anchorProvider, publicKey, {
35380
+ app_id,
35381
+ documents: solTransactionData.txArgs[0].setDocumentData,
35382
+ delete_paths: solTransactionData.txArgs[0].deletePaths,
35383
+ txData: solTransactionData.txArgs[0].txData
35384
+ }, finalDeduped, solTransactionData.lutKey, solTransactionData.preInstructions, false);
35385
+ if ((options === null || options === void 0 ? void 0 : options.shouldSubmitTx) === false) {
35386
+ const signedTx = await walletAdapter.signTransaction(tx);
35387
+ return {
35388
+ signedTransaction: signedTx,
35389
+ blockNumber: 0,
35390
+ gasUsed: '0',
35391
+ data: ''
35392
+ };
35393
+ }
35394
+ // On surfnet, sign + submit manually
35395
+ if (isSurfnet) {
35396
+ const signedTx = await walletAdapter.signTransaction(tx);
35397
+ const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');
35398
+ const signature = await connection.sendRawTransaction(signedTx.serialize(), {
35399
+ preflightCommitment: 'confirmed'
35400
+ });
35401
+ const confirmation = await connection.confirmTransaction({
35402
+ signature,
35403
+ blockhash,
35404
+ lastValidBlockHeight,
35405
+ }, 'confirmed');
35406
+ if (confirmation.value.err) {
35407
+ throw new Error(`Transaction failed: ${confirmation.value.err.toString()}`);
35408
+ }
35409
+ const txInfo = await connection.getParsedTransaction(signature, {
35410
+ maxSupportedTransactionVersion: 0,
35411
+ commitment: 'confirmed'
35412
+ });
35413
+ return {
35414
+ transactionSignature: signature,
35415
+ blockNumber: (txInfo === null || txInfo === void 0 ? void 0 : txInfo.slot) || 0,
35416
+ gasUsed: ((_a = txInfo === null || txInfo === void 0 ? void 0 : txInfo.meta) === null || _a === void 0 ? void 0 : _a.fee.toString()) || '0',
35417
+ data: txInfo === null || txInfo === void 0 ? void 0 : txInfo.meta,
35418
+ };
35419
+ }
35420
+ // Use MWA's sendTransaction for mobile-optimized submission
35421
+ let signature;
35422
+ if (adapter.sendTransaction) {
35423
+ signature = await adapter.sendTransaction(tx, connection, {
35424
+ preflightCommitment: 'confirmed',
35425
+ });
35426
+ }
35427
+ else {
35428
+ const signedTx = await walletAdapter.signTransaction(tx);
35429
+ signature = await connection.sendRawTransaction(signedTx.serialize(), {
35430
+ preflightCommitment: 'confirmed'
35431
+ });
35432
+ }
35433
+ const txInfo = await confirmAndCheckTransaction(connection, signature);
35434
+ return {
35435
+ transactionSignature: signature,
35436
+ blockNumber: (txInfo === null || txInfo === void 0 ? void 0 : txInfo.slot) || 0,
35437
+ gasUsed: ((_b = txInfo === null || txInfo === void 0 ? void 0 : txInfo.meta) === null || _b === void 0 ? void 0 : _b.fee.toString()) || '0',
35438
+ data: txInfo === null || txInfo === void 0 ? void 0 : txInfo.meta,
35439
+ };
35440
+ }
35441
+ catch (error) {
35442
+ 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'))) {
35443
+ await this.logout();
35444
+ throw new Error('Wallet connection lost. Please reconnect.');
35445
+ }
35446
+ const isUserRejection = (error === null || error === void 0 ? void 0 : error.code) === 4001 ||
35447
+ ((_e = error === null || error === void 0 ? void 0 : error.message) === null || _e === void 0 ? void 0 : _e.toLowerCase().includes('user rejected')) ||
35448
+ ((_f = error === null || error === void 0 ? void 0 : error.message) === null || _f === void 0 ? void 0 : _f.toLowerCase().includes('user denied')) ||
35449
+ ((_g = error === null || error === void 0 ? void 0 : error.message) === null || _g === void 0 ? void 0 : _g.toLowerCase().includes('user cancelled')) ||
35450
+ ((_h = error === null || error === void 0 ? void 0 : error.message) === null || _h === void 0 ? void 0 : _h.toLowerCase().includes('user canceled'));
35451
+ if (!isUserRejection) {
35452
+ console.error('[SolanaMobileWallet] Transaction failed:', error);
35453
+ }
35454
+ throw new Error(`Failed to execute transaction: ${error.message}`);
35455
+ }
35456
+ }
35457
+ async getNativeMethods() {
35458
+ return this.mwaAdapter;
35459
+ }
35460
+ /* ----------------------------------------------------------- *
35461
+ * Private Helpers
35462
+ * ----------------------------------------------------------- */
35463
+ getRpcUrl(network) {
35464
+ if (this.networkUrl) {
35465
+ return this.networkUrl;
35466
+ }
35467
+ if (network === 'solana_devnet') {
35468
+ return SOLANA_DEVNET_RPC_URL;
35469
+ }
35470
+ else if (network === 'solana_mainnet') {
35471
+ return SOLANA_MAINNET_RPC_URL;
35472
+ }
35473
+ else if (network === 'surfnet') {
35474
+ return SURFNET_RPC_URL;
35475
+ }
35476
+ return SOLANA_MAINNET_RPC_URL;
35477
+ }
35478
+ }
35479
+ SolanaMobileWalletProvider.instance = null;
35480
+
34906
35481
  let currentAuthProvider = null;
34907
35482
  let currentAuthMethod = null;
34908
35483
  let initConfig = null;
@@ -34972,6 +35547,30 @@ async function hotSwapToPrivyProvider(config) {
34972
35547
  setAuthProviderInstance(provider);
34973
35548
  return privyProvider;
34974
35549
  }
35550
+ async function hotSwapToMWAProvider(config) {
35551
+ var _a, _b;
35552
+ const rpcUrl = (_a = config.rpcUrl) !== null && _a !== void 0 ? _a : null;
35553
+ const mwaConfig = (_b = config.mobileWalletConfig) !== null && _b !== void 0 ? _b : {
35554
+ appIdentity: {
35555
+ name: config.name || undefined,
35556
+ uri: typeof window !== 'undefined' ? window.location.origin : undefined,
35557
+ },
35558
+ };
35559
+ const mwaProvider = new SolanaMobileWalletProvider(rpcUrl, mwaConfig);
35560
+ let provider = mwaProvider;
35561
+ if (config.chain === "offchain") {
35562
+ provider = new OffchainAuthProvider(mwaProvider);
35563
+ }
35564
+ currentAuthProvider = provider;
35565
+ currentAuthMethod = 'mobile-wallet-adapter';
35566
+ const coreConfig = await getConfig();
35567
+ coreConfig.authProvider = provider;
35568
+ // Send 'phantom' as authMethod so the server routes to Solana verification.
35569
+ // TODO: change back to 'mobile-wallet-adapter' once the backend is deployed.
35570
+ coreConfig.authMethod = 'phantom';
35571
+ setAuthProviderInstance(provider);
35572
+ return provider;
35573
+ }
34975
35574
  const SOLANA_DEVNET_RPC_URL = "https://idelle-8nxsep-fast-devnet.helius-rpc.com";
34976
35575
  const SOLANA_MAINNET_RPC_URL = "https://celestia-cegncv-fast-mainnet.helius-rpc.com";
34977
35576
  const SURFNET_RPC_URL = "https://surfpool.fly.dev";
@@ -34991,7 +35590,7 @@ async function getAuthProvider(config) {
34991
35590
  return currentAuthProvider;
34992
35591
  }
34993
35592
  // If the user previously logged in with a specific method, use that instead
34994
- const validAuthMethods = ['privy', 'phantom', 'wallet', 'rainbowkit', 'coinbase-smart-wallet', 'onboard', 'none'];
35593
+ const validAuthMethods = ['privy', 'phantom', 'wallet', 'rainbowkit', 'coinbase-smart-wallet', 'onboard', 'mobile-wallet-adapter', 'none'];
34995
35594
  const storedMethod = getStoredAuthMethod();
34996
35595
  const authMethod = (storedMethod && validAuthMethods.includes(storedMethod))
34997
35596
  ? storedMethod
@@ -35017,8 +35616,13 @@ async function getAuthProvider(config) {
35017
35616
  if ((_d = config.phantomConfig) === null || _d === void 0 ? void 0 : _d.enablePrivyFallback) {
35018
35617
  currentAuthProvider.onSwitchToPrivy =
35019
35618
  () => hotSwapToPrivyProvider(config);
35619
+ currentAuthProvider.onSwitchToMWA =
35620
+ () => hotSwapToMWAProvider(config);
35020
35621
  }
35021
35622
  break;
35623
+ case "mobile-wallet-adapter":
35624
+ currentAuthProvider = new SolanaMobileWalletProvider(rpcUrl, config.mobileWalletConfig);
35625
+ break;
35022
35626
  case "onboard":
35023
35627
  console.warn("Onboard auth is not yet supported.");
35024
35628
  break;
@@ -35241,6 +35845,7 @@ exports.OffchainAuthProvider = OffchainAuthProvider;
35241
35845
  exports.PhantomWalletProvider = PhantomWalletProvider;
35242
35846
  exports.PrivyWalletProvider = PrivyWalletProvider;
35243
35847
  exports.ServerSessionManager = ServerSessionManager;
35848
+ exports.SolanaMobileWalletProvider = SolanaMobileWalletProvider;
35244
35849
  exports.WebSessionManager = WebSessionManager;
35245
35850
  exports.aggregate = aggregate;
35246
35851
  exports.bs58 = bs58;
@@ -35263,12 +35868,14 @@ exports.getCurrentUser = getCurrentUser;
35263
35868
  exports.getFiles = getFiles;
35264
35869
  exports.getIdToken = getIdToken;
35265
35870
  exports.init = init;
35871
+ exports.isMobileWalletAvailable = isMobileWalletAvailable;
35266
35872
  exports.login = login;
35267
35873
  exports.logout = logout;
35268
35874
  exports.onAuthLoadingChanged = onAuthLoadingChanged;
35269
35875
  exports.onAuthStateChanged = onAuthStateChanged;
35270
35876
  exports.reconnectWithNewAuth = reconnectWithNewAuth;
35271
35877
  exports.refreshSession = refreshSession;
35878
+ exports.registerMobileWalletAdapter = registerMobileWalletAdapter;
35272
35879
  exports.runExpression = runExpression;
35273
35880
  exports.runExpressionMany = runExpressionMany;
35274
35881
  exports.runQuery = runQuery;
@@ -35282,4 +35889,4 @@ exports.signSessionCreateMessage = signSessionCreateMessage;
35282
35889
  exports.signTransaction = signTransaction;
35283
35890
  exports.subscribe = subscribe;
35284
35891
  exports.useAuth = useAuth;
35285
- //# sourceMappingURL=index-C-ADfv7y.js.map
35892
+ //# sourceMappingURL=index-CMKICH1P.js.map