/* ─── Sitewide Solana Wallet Connect ───────────────────────── Global wallet connection module for jaeswift.xyz Provides window.solWallet API + custom events ─────────────────────────────────────────────────────────── */ (function () { 'use strict'; const LS_WALLET_NAME = 'sol_wallet_name'; const LS_WALLET_ADDR = 'sol_wallet_address'; // ── State ── const state = { connected: false, address: null, provider: null, _walletName: null, _isWalletStandard: false, }; // ── Wallet Standard Discovery ── const walletStandardWallets = []; function initWalletStandard() { // Listen for wallets registering AFTER us window.addEventListener('wallet-standard:register-wallet', (event) => { try { const callback = event.detail; if (typeof callback === 'function') { callback({ register: (...wallets) => { for (const w of wallets) { if (!walletStandardWallets.some(e => e.name === w.name)) { walletStandardWallets.push(w); } } } }); } } catch (e) { /* silent */ } }); // Announce app-ready — catches wallets loaded BEFORE us try { window.dispatchEvent(new CustomEvent('wallet-standard:app-ready', { detail: { register: (...wallets) => { for (const w of wallets) { if (!walletStandardWallets.some(e => e.name === w.name)) { walletStandardWallets.push(w); } } } } })); } catch (e) { /* silent */ } } initWalletStandard(); // ── Known wallets for install links ── const KNOWN_WALLETS = [ { name: 'PHANTOM', icon: '👻', url: 'https://phantom.app' }, { name: 'JUPITER', icon: '🪐', url: 'https://jup.ag' }, { name: 'SOLFLARE', icon: '🔆', url: 'https://solflare.com' }, { name: 'BACKPACK', icon: '🎒', url: 'https://backpack.app' }, { name: 'COINBASE', icon: '🔵', url: 'https://coinbase.com/wallet' }, { name: 'TRUST', icon: '🛡️', url: 'https://trustwallet.com' }, { name: 'METAMASK', icon: '🦊', url: 'https://metamask.io' }, ]; // ── Wallet detection (reused from soldomains.js) ── function getAvailableWallets() { const found = []; const seen = new Set(); function add(name, icon, provider, url, isWS) { if (!provider || seen.has(name)) return; seen.add(name); found.push({ name, icon, provider, url, isWalletStandard: !!isWS }); } // 1. Wallet Standard wallets for (const w of walletStandardWallets) { try { if (!w.features?.['standard:connect']) continue; const name = (w.name || 'WALLET').toUpperCase(); let icon = '◈'; if (name.includes('METAMASK')) icon = '🦊'; else if (name.includes('PHANTOM')) icon = '👻'; else if (name.includes('SOLFLARE')) icon = '🔆'; else if (name.includes('BACKPACK')) icon = '🎒'; else if (name.includes('COINBASE')) icon = '🔵'; else if (name.includes('TRUST')) icon = '🛡️'; else if (name.includes('JUPITER')) icon = '🪐'; add(name, icon, w, w.url || '#', true); } catch (e) { /* silent */ } } // 2. Legacy injection points try { const p = window.phantom?.solana; if (p && p.isPhantom) add('PHANTOM', '👻', p, 'https://phantom.app'); } catch (e) {} try { const s = window.solflare; if (s && s.isSolflare) add('SOLFLARE', '🔆', s, 'https://solflare.com'); } catch (e) {} try { const b = window.backpack; if (b && b.isBackpack) add('BACKPACK', '🎒', b, 'https://backpack.app'); } catch (e) {} try { if (window.coinbaseSolana) add('COINBASE', '🔵', window.coinbaseSolana, 'https://coinbase.com/wallet'); } catch (e) {} try { const t = window.trustwallet?.solana; if (t) add('TRUST', '🛡️', t, 'https://trustwallet.com'); } catch (e) {} // 3. Generic window.solana try { const ws = window.solana; if (ws) { if (ws.isJupiter && !seen.has('JUPITER')) add('JUPITER', '🪐', ws, 'https://jup.ag'); else if (ws.isMetaMask && !seen.has('METAMASK')) add('METAMASK', '🦊', ws, 'https://metamask.io'); else if (!ws.isPhantom && !ws.isSolflare && !ws.isBackpack && !seen.has('PHANTOM') && !seen.has('METAMASK')) add('SOLANA WALLET', '◈', ws, '#'); } } catch (e) {} // 4. Jupiter specific try { const j = window.jupiter?.solana; if (j) add('JUPITER', '🪐', j, 'https://jup.ag'); } catch (e) {} // 5. Multi-provider array try { const providers = window.solana?.providers; if (Array.isArray(providers)) { for (const p of providers) { if (p.isPhantom) add('PHANTOM', '👻', p, 'https://phantom.app'); else if (p.isJupiter) add('JUPITER', '🪐', p, 'https://jup.ag'); else if (p.isSolflare) add('SOLFLARE', '🔆', p, 'https://solflare.com'); else if (p.isBackpack) add('BACKPACK', '🎒', p, 'https://backpack.app'); else if (p.isMetaMask) add('METAMASK', '🦊', p, 'https://metamask.io'); } } } catch (e) {} return found; } // ── Connect ── async function connect(walletObj) { // If called without arg, open picker (handled by nav UI) // If called with a wallet name string, find it if (typeof walletObj === 'string') { const available = getAvailableWallets(); walletObj = available.find(w => w.name === walletObj); if (!walletObj) throw new Error('Wallet not found: ' + walletObj); } if (!walletObj) throw new Error('No wallet specified'); try { let address; if (walletObj.isWalletStandard) { const connectFeature = walletObj.provider.features['standard:connect']; const result = await connectFeature.connect(); const accounts = result.accounts || []; if (accounts.length === 0) throw new Error('No accounts returned'); address = accounts[0].address; } else { const resp = await walletObj.provider.connect(); address = resp.publicKey.toString(); } state.connected = true; state.address = address; state.provider = walletObj.provider; state._walletName = walletObj.name; state._isWalletStandard = walletObj.isWalletStandard; // Persist localStorage.setItem(LS_WALLET_NAME, walletObj.name); localStorage.setItem(LS_WALLET_ADDR, address); // Dispatch event window.dispatchEvent(new CustomEvent('wallet-connected', { detail: { address, walletName: walletObj.name } })); return { address, walletName: walletObj.name }; } catch (err) { console.warn('[solWallet] Connect failed:', err.message); throw err; } } // ── Disconnect ── function disconnect() { if (state.provider) { try { if (state._isWalletStandard) { const disconnectFeature = state.provider.features?.['standard:disconnect']; if (disconnectFeature) disconnectFeature.disconnect(); } else { state.provider.disconnect(); } } catch (e) { /* silent */ } } const prevAddr = state.address; state.connected = false; state.address = null; state.provider = null; state._walletName = null; state._isWalletStandard = false; localStorage.removeItem(LS_WALLET_NAME); localStorage.removeItem(LS_WALLET_ADDR); window.dispatchEvent(new CustomEvent('wallet-disconnected', { detail: { address: prevAddr } })); } // ── Silent auto-reconnect ── async function autoReconnect() { const savedName = localStorage.getItem(LS_WALLET_NAME); if (!savedName) return; // Small delay to let wallet extensions inject await new Promise(r => setTimeout(r, 600)); const available = getAvailableWallets(); const wallet = available.find(w => w.name === savedName); if (!wallet) { // Wallet no longer available — clear saved state quietly localStorage.removeItem(LS_WALLET_NAME); localStorage.removeItem(LS_WALLET_ADDR); return; } try { await connect(wallet); } catch (e) { // Auto-reconnect failed — clear saved state quietly localStorage.removeItem(LS_WALLET_NAME); localStorage.removeItem(LS_WALLET_ADDR); state.connected = false; state.address = null; state.provider = null; state._walletName = null; } } // ── Truncate address helper ── function truncAddr(a) { if (!a || a.length < 12) return a || ''; return a.slice(0, 4) + '...' + a.slice(-4); } // ── Public API ── window.solWallet = { get connected() { return state.connected; }, get address() { return state.address; }, get provider() { return state.provider; }, get walletName() { return state._walletName; }, get isWalletStandard() { return state._isWalletStandard; }, connect, disconnect, getAvailableWallets, truncAddr, KNOWN_WALLETS, _autoReconnect: autoReconnect, }; // Kick off auto-reconnect when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', autoReconnect); } else { autoReconnect(); } })();