/** * XIC Multi-Wallet Connector v2.1 * 支持:MetaMask、TronLink、OKX Wallet、Trust Wallet、TokenPocket、Bitget Wallet、Phantom * 修复:MetaMask 连接失败、缓存问题、移动端兼容 */ (function () { 'use strict'; // ─── 样式 ───────────────────────────────────────────────────────────────────── const STYLES = ` @keyframes xicFadeIn { from { opacity:0; transform:translateY(-10px) scale(0.97); } to { opacity:1; transform:translateY(0) scale(1); } } @keyframes xicOverlayIn { from { opacity:0; } to { opacity:1; } } @keyframes xicToastIn { from { opacity:0; transform:translateX(-50%) translateY(20px); } to { opacity:1; transform:translateX(-50%) translateY(0); } } @keyframes xicSpin { from { transform:rotate(0deg); } to { transform:rotate(360deg); } } #xic-wallet-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.75); backdrop-filter: blur(6px); z-index: 999998; align-items: center; justify-content: center; animation: xicOverlayIn 0.25s ease; } #xic-wallet-overlay.open { display: flex; } #xic-wallet-modal { background: linear-gradient(145deg, #1a1b2e, #16213e); border: 1px solid rgba(99,102,241,0.3); border-radius: 20px; padding: 28px; width: 420px; max-width: calc(100vw - 32px); max-height: 90vh; overflow-y: auto; box-shadow: 0 25px 60px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.05); animation: xicFadeIn 0.3s cubic-bezier(0.34,1.56,0.64,1); font-family: 'Plus Jakarta Sans', 'Inter', -apple-system, sans-serif; } #xic-wallet-modal::-webkit-scrollbar { width: 4px; } #xic-wallet-modal::-webkit-scrollbar-track { background: transparent; } #xic-wallet-modal::-webkit-scrollbar-thumb { background: rgba(99,102,241,0.4); border-radius: 2px; } .xic-modal-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; } .xic-modal-title { font-size: 20px; font-weight: 700; color: #fff; letter-spacing: -0.3px; } .xic-modal-subtitle { font-size: 13px; color: rgba(255,255,255,0.5); margin-bottom: 20px; } #xic-close-btn { background: rgba(255,255,255,0.08); border: none; color: rgba(255,255,255,0.7); width: 32px; height: 32px; border-radius: 8px; cursor: pointer; font-size: 16px; display: flex; align-items: center; justify-content: center; transition: all 0.2s; flex-shrink: 0; } #xic-close-btn:hover { background: rgba(255,255,255,0.15); color: #fff; } .xic-wallet-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 16px; } .xic-wallet-btn { background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.08); border-radius: 14px; padding: 14px 12px; cursor: pointer; display: flex; flex-direction: column; align-items: center; gap: 8px; transition: all 0.2s; position: relative; text-align: center; } .xic-wallet-btn:hover { background: rgba(99,102,241,0.15); border-color: rgba(99,102,241,0.4); transform: translateY(-2px); box-shadow: 0 8px 24px rgba(99,102,241,0.2); } .xic-wallet-btn:active { transform: translateY(0); } .xic-wallet-btn.loading { pointer-events: none; opacity: 0.7; } .xic-wallet-btn.detected { border-color: rgba(16,185,129,0.4); background: rgba(16,185,129,0.06); } .xic-wallet-icon { width: 44px; height: 44px; border-radius: 12px; overflow: hidden; flex-shrink: 0; } .xic-wallet-icon svg, .xic-wallet-icon img { width: 100%; height: 100%; } .xic-wallet-name { font-size: 13px; font-weight: 600; color: #fff; line-height: 1.2; } .xic-wallet-desc { font-size: 11px; color: rgba(255,255,255,0.45); line-height: 1.3; } .xic-wallet-network { font-size: 10px; font-weight: 600; padding: 2px 8px; border-radius: 20px; background: rgba(99,102,241,0.2); color: rgba(99,102,241,0.9); letter-spacing: 0.3px; } .xic-detected-dot { position: absolute; top: 10px; right: 10px; width: 8px; height: 8px; background: #10b981; border-radius: 50%; box-shadow: 0 0 6px #10b981; } .xic-spinner { width: 20px; height: 20px; border: 2px solid rgba(255,255,255,0.2); border-top-color: #fff; border-radius: 50%; animation: xicSpin 0.8s linear infinite; margin: 0 auto; } .xic-modal-footer { text-align: center; font-size: 12px; color: rgba(255,255,255,0.35); padding-top: 12px; border-top: 1px solid rgba(255,255,255,0.06); } .xic-modal-footer a { color: rgba(99,102,241,0.8); text-decoration: none; } .xic-modal-footer a:hover { color: #818cf8; text-decoration: underline; } .xic-install-hint { background: rgba(245,158,11,0.1); border: 1px solid rgba(245,158,11,0.25); border-radius: 12px; padding: 12px 14px; margin-top: 12px; font-size: 12px; color: rgba(255,255,255,0.7); display: none; } .xic-install-hint.show { display: block; } .xic-install-hint strong { color: #fbbf24; } .xic-install-hint a { color: #fbbf24; font-weight: 600; text-decoration: none; } .xic-install-hint a:hover { text-decoration: underline; } .xic-connected-banner { background: linear-gradient(135deg, rgba(16,185,129,0.15), rgba(5,150,105,0.1)); border: 1px solid rgba(16,185,129,0.3); border-radius: 12px; padding: 12px 16px; margin-bottom: 16px; display: none; align-items: center; gap: 10px; } .xic-connected-banner.show { display: flex; } .xic-connected-dot { width: 10px; height: 10px; background: #10b981; border-radius: 50%; box-shadow: 0 0 8px #10b981; flex-shrink: 0; } .xic-connected-text { flex: 1; } .xic-connected-label { font-size: 11px; color: rgba(255,255,255,0.5); } .xic-connected-addr { font-size: 13px; font-weight: 600; color: #10b981; font-family: monospace; } .xic-disconnect-btn { background: rgba(239,68,68,0.15); border: 1px solid rgba(239,68,68,0.3); color: #f87171; border-radius: 8px; padding: 4px 10px; font-size: 11px; cursor: pointer; transition: all 0.2s; } .xic-disconnect-btn:hover { background: rgba(239,68,68,0.25); } `; // ─── 钱包配置 ───────────────────────────────────────────────────────────────── const WALLETS = [ { id: 'metamask', name: 'MetaMask', desc: '最流行的以太坊钱包', network: 'ETH/BSC', installUrl: 'https://metamask.io/download/', icon: ``, detect: () => { // 检测 MetaMask:window.ethereum.isMetaMask 或 window.ethereum providers 数组 if (window.ethereum) { if (window.ethereum.isMetaMask) return true; if (window.ethereum.providers) { return window.ethereum.providers.some(p => p.isMetaMask); } } return false; }, connect: connectMetaMask, }, { id: 'tronlink', name: 'TronLink', desc: 'TRON 官方钱包', network: 'TRON', installUrl: 'https://www.tronlink.org/', icon: ``, detect: () => !!(window.tronLink || (window.tronWeb && window.tronWeb.ready)), connect: connectTronLink, }, { id: 'okx', name: 'OKX Wallet', desc: 'OKX 多链钱包', network: 'TRON/ETH', installUrl: 'https://www.okx.com/web3', icon: ``, detect: () => !!(window.okxwallet || (window.ethereum && window.ethereum.isOkxWallet)), connect: connectOKX, }, { id: 'trust', name: 'Trust Wallet', desc: '币安官方钱包', network: 'TRON/ETH', installUrl: 'https://trustwallet.com/download', icon: ``, detect: () => !!(window.trustwallet || (window.ethereum && window.ethereum.isTrust)), connect: connectTrust, }, { id: 'tokenpocket', name: 'TokenPocket', desc: '多链 DeFi 钱包', network: 'TRON/ETH', installUrl: 'https://www.tokenpocket.pro/', icon: ``, detect: () => !!(window.tokenpocket || (window.ethereum && window.ethereum.isTokenPocket)), connect: connectTokenPocket, }, { id: 'bitget', name: 'Bitget Wallet', desc: 'Bitget 交易所钱包', network: 'TRON/ETH', installUrl: 'https://web3.bitget.com/zh-CN/wallet-download', icon: ``, detect: () => !!(window.bitkeep || (window.ethereum && window.ethereum.isBitKeep)), connect: connectBitget, }, { id: 'phantom', name: 'Phantom', desc: 'Solana & 多链钱包', network: 'SOL/ETH', installUrl: 'https://phantom.app/download', icon: ``, detect: () => !!(window.phantom), connect: connectPhantom, }, ]; // ─── 状态 ───────────────────────────────────────────────────────────────────── let currentWallet = null; let currentAddress = null; let modalEl = null; let overlayEl = null; let isInjected = false; let installHintEl = null; let connectedBannerEl = null; // ─── 工具函数 ───────────────────────────────────────────────────────────────── function shortenAddr(addr) { if (!addr) return ''; if (addr.length > 20) return addr.slice(0, 8) + '...' + addr.slice(-6); return addr; } function showToast(msg, type = 'info') { const colors = { info: '#6366f1', success: '#10b981', error: '#ef4444', warn: '#f59e0b' }; // 移除已有 toast document.querySelectorAll('.xic-toast').forEach(t => t.remove()); const toast = document.createElement('div'); toast.className = 'xic-toast'; toast.style.cssText = ` position:fixed; bottom:28px; left:50%; transform:translateX(-50%); background:${colors[type] || colors.info}; color:#fff; padding:12px 24px; border-radius:12px; font-size:14px; font-weight:600; z-index:9999999; box-shadow:0 8px 32px rgba(0,0,0,0.4); font-family:'Plus Jakarta Sans','Inter',sans-serif; animation: xicToastIn 0.3s ease; pointer-events:none; white-space:nowrap; max-width:90vw; `; toast.textContent = msg; document.body.appendChild(toast); setTimeout(() => toast.remove(), 4000); } function getEthProvider() { // 获取 MetaMask provider(处理多钱包共存的情况) if (!window.ethereum) return null; if (window.ethereum.isMetaMask) return window.ethereum; // 多钱包共存时,从 providers 数组中找 MetaMask if (window.ethereum.providers) { const mm = window.ethereum.providers.find(p => p.isMetaMask); if (mm) return mm; } return null; } // ─── 钱包连接函数 ───────────────────────────────────────────────────────────── async function connectMetaMask() { const provider = getEthProvider(); if (!provider) { // 检测是否是移动端 const isMobile = /Android|iPhone|iPad|iPod/i.test(navigator.userAgent); if (isMobile) { // 移动端:尝试通过 MetaMask 深链接打开 console.log("[XIC] MetaMask mobile deeplink blocked"); throw new Error('正在跳转到 MetaMask App...'); } // 桌面端:显示安装提示 showInstallHint('metamask'); throw new Error('MetaMask 未安装'); } try { // 请求账户授权 const accounts = await provider.request({ method: 'eth_requestAccounts' }); if (!accounts || accounts.length === 0) { throw new Error('用户拒绝了连接请求'); } const address = accounts[0]; // 获取当前链信息 let chainId = null; try { chainId = await provider.request({ method: 'eth_chainId' }); } catch (e) {} console.log('[XIC Wallet] MetaMask 连接成功:', address, 'chainId:', chainId); return { address, type: 'eth', chainId }; } catch (err) { if (err.code === 4001) { throw new Error('用户取消了连接'); } if (err.code === -32002) { throw new Error('MetaMask 正在等待确认,请打开 MetaMask 扩展'); } throw err; } } async function connectTronLink() { // 方式1:新版 TronLink API if (window.tronLink) { try { const res = await window.tronLink.request({ method: 'tron_requestAccounts' }); if (res && (res.code === 200 || res.code === 4000)) { await new Promise(r => setTimeout(r, 500)); const addr = window.tronWeb?.defaultAddress?.base58; if (addr) return { address: addr, type: 'tron' }; } } catch (e) { console.warn('[XIC Wallet] TronLink new API failed:', e); } } // 方式2:旧版 tronWeb if (window.tronWeb && window.tronWeb.ready) { const addr = window.tronWeb.defaultAddress?.base58; if (addr) return { address: addr, type: 'tron' }; } // 未安装 showInstallHint('tronlink'); throw new Error('请先安装 TronLink 钱包'); } async function connectOKX() { // OKX TRON if (window.okxwallet?.tronLink) { try { const res = await window.okxwallet.tronLink.request({ method: 'tron_requestAccounts' }); if (res?.code === 200) { const addr = window.okxwallet.tronLink.tronWeb?.defaultAddress?.base58; if (addr) return { address: addr, type: 'tron' }; } } catch (e) {} } // OKX ETH if (window.okxwallet) { try { const accounts = await window.okxwallet.request({ method: 'eth_requestAccounts' }); if (accounts?.[0]) return { address: accounts[0], type: 'eth' }; } catch (e) {} } // 通用 ethereum(OKX 注入) if (window.ethereum?.isOkxWallet) { const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' }); if (accounts?.[0]) return { address: accounts[0], type: 'eth' }; } showInstallHint('okx'); throw new Error('请先安装 OKX Wallet'); } async function connectTrust() { if (window.trustwallet) { try { const accounts = await window.trustwallet.request({ method: 'eth_requestAccounts' }); if (accounts?.[0]) return { address: accounts[0], type: 'eth' }; } catch (e) {} } if (window.ethereum?.isTrust) { const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' }); if (accounts?.[0]) return { address: accounts[0], type: 'eth' }; } // 移动端深链接 const isMobile = /Android|iPhone|iPad|iPod/i.test(navigator.userAgent); if (isMobile) { console.log("[XIC] Trust Wallet deeplink blocked"); throw new Error('正在跳转到 Trust Wallet...'); } showInstallHint('trust'); throw new Error('请先安装 Trust Wallet'); } async function connectTokenPocket() { if (window.tokenpocket) { try { const accounts = await window.tokenpocket.request({ method: 'eth_requestAccounts' }); if (accounts?.[0]) return { address: accounts[0], type: 'eth' }; } catch (e) {} } if (window.ethereum?.isTokenPocket) { const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' }); if (accounts?.[0]) return { address: accounts[0], type: 'eth' }; } showInstallHint('tokenpocket'); throw new Error('请先安装 TokenPocket'); } async function connectBitget() { if (window.bitkeep?.ethereum) { try { const accounts = await window.bitkeep.ethereum.request({ method: 'eth_requestAccounts' }); if (accounts?.[0]) return { address: accounts[0], type: 'eth' }; } catch (e) {} } if (window.ethereum?.isBitKeep) { const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' }); if (accounts?.[0]) return { address: accounts[0], type: 'eth' }; } showInstallHint('bitget'); throw new Error('请先安装 Bitget Wallet'); } async function connectPhantom() { if (window.phantom?.ethereum) { try { const accounts = await window.phantom.ethereum.request({ method: 'eth_requestAccounts' }); if (accounts?.[0]) return { address: accounts[0], type: 'eth' }; } catch (e) {} } if (window.phantom?.solana) { try { const resp = await window.phantom.solana.connect(); if (resp?.publicKey) return { address: resp.publicKey.toString(), type: 'sol' }; } catch (e) {} } showInstallHint('phantom'); throw new Error('请先安装 Phantom 钱包'); } // ─── 安装提示 ───────────────────────────────────────────────────────────────── function showInstallHint(walletId) { const wallet = WALLETS.find(w => w.id === walletId); if (!wallet || !installHintEl) return; installHintEl.innerHTML = ` ⚠️ ${wallet.name} 未安装
请先安装 ${wallet.name} 钱包扩展,然后刷新页面重试。
👉 点击下载 ${wallet.name} `; installHintEl.classList.add('show'); } // ─── 连接成功处理 ───────────────────────────────────────────────────────────── function onConnected(address, wallet) { currentAddress = address; currentWallet = wallet; // 更新 modal 内的已连接横幅 if (connectedBannerEl) { connectedBannerEl.querySelector('.xic-connected-addr').textContent = shortenAddr(address); connectedBannerEl.querySelector('.xic-connected-label').textContent = `已连接 ${wallet.name}`; connectedBannerEl.classList.add('show'); } // 更新导航栏按钮 updateNavbarBtn(address); // 关闭弹窗 setTimeout(() => closeModal(), 800); // 显示成功提示 showToast(`✅ ${wallet.name} 连接成功:${shortenAddr(address)}`, 'success'); // 监听账户变更 if (wallet.id === 'metamask') { const provider = getEthProvider(); if (provider) { provider.on('accountsChanged', (accounts) => { if (accounts.length === 0) { onDisconnected(); } else { currentAddress = accounts[0]; updateNavbarBtn(accounts[0]); } }); } } } function onDisconnected() { currentAddress = null; currentWallet = null; if (connectedBannerEl) connectedBannerEl.classList.remove('show'); updateNavbarBtn(null); showToast('钱包已断开连接', 'warn'); } function updateNavbarBtn(address) { const btn = findConnectBtn(); if (!btn) return; if (address) { btn.textContent = shortenAddr(address); btn.style.cssText = ` background: linear-gradient(135deg, #10b981, #059669) !important; color: #fff !important; border: none !important; border-radius: 10px !important; padding: 10px 18px !important; font-weight: 700 !important; cursor: pointer !important; font-size: 13px !important; `; } else { btn.textContent = 'Connect Wallet'; btn.style.cssText = ''; } } // ─── 渲染钱包列表 ───────────────────────────────────────────────────────────── function renderWalletGrid() { const grid = modalEl.querySelector('.xic-wallet-grid'); if (!grid) return; grid.innerHTML = ''; WALLETS.forEach(wallet => { const detected = wallet.detect(); const btn = document.createElement('button'); btn.className = 'xic-wallet-btn' + (detected ? ' detected' : ''); btn.innerHTML = ` ${detected ? '' : ''}
${wallet.icon}
${wallet.name}
${wallet.desc}
${wallet.network}
`; btn.addEventListener('click', () => handleWalletClick(wallet, btn)); grid.appendChild(btn); }); } async function handleWalletClick(wallet, btn) { // 隐藏安装提示 if (installHintEl) installHintEl.classList.remove('show'); // 显示加载状态 const originalHTML = btn.innerHTML; btn.classList.add('loading'); btn.innerHTML = `
连接中...
`; try { const result = await wallet.connect(); onConnected(result.address, wallet); } catch (err) { console.error('[XIC Wallet] 连接失败:', err); // 恢复按钮状态 btn.classList.remove('loading'); btn.innerHTML = originalHTML; const msg = err.message || '连接失败'; if (!msg.includes('未安装') && !msg.includes('Not installed')) { showToast('❌ ' + msg, 'error'); } } } // ─── 创建弹窗 ───────────────────────────────────────────────────────────────── function createModal() { // 注入样式 if (!document.getElementById('xic-wallet-styles')) { const style = document.createElement('style'); style.id = 'xic-wallet-styles'; style.textContent = STYLES; document.head.appendChild(style); } // 创建遮罩层 overlayEl = document.createElement('div'); overlayEl.id = 'xic-wallet-overlay'; // 创建弹窗 modalEl = document.createElement('div'); modalEl.id = 'xic-wallet-modal'; modalEl.innerHTML = `
连接钱包
选择您的钱包以继续购买 XIC
已连接
`; overlayEl.appendChild(modalEl); document.body.appendChild(overlayEl); // 获取引用 connectedBannerEl = modalEl.querySelector('.xic-connected-banner'); installHintEl = modalEl.querySelector('.xic-install-hint'); // 关闭按钮 modalEl.querySelector('#xic-close-btn').addEventListener('click', closeModal); // 断开连接按钮 modalEl.querySelector('.xic-disconnect-btn').addEventListener('click', () => { onDisconnected(); closeModal(); }); // 点击遮罩关闭 overlayEl.addEventListener('click', (e) => { if (e.target === overlayEl) closeModal(); }); // ESC 关闭 document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closeModal(); }); // 渲染钱包列表 renderWalletGrid(); } // ─── 弹窗控制 ───────────────────────────────────────────────────────────────── function openModal() { if (!overlayEl) createModal(); overlayEl.classList.add('open'); // 刷新检测状态 renderWalletGrid(); // 如果已连接,显示已连接状态 if (currentAddress && connectedBannerEl) { connectedBannerEl.querySelector('.xic-connected-addr').textContent = shortenAddr(currentAddress); connectedBannerEl.querySelector('.xic-connected-label').textContent = `已连接 ${currentWallet?.name || ''}`; connectedBannerEl.classList.add('show'); } if (installHintEl) installHintEl.classList.remove('show'); } function closeModal() { if (overlayEl) overlayEl.classList.remove('open'); } // ─── 找到连接按钮 ───────────────────────────────────────────────────────────── function findConnectBtn() { const selectors = [ 'button[class*="wallet"]', 'button[class*="connect"]', 'button[class*="Connect"]', ]; // 先用文本匹配 const allBtns = document.querySelectorAll('button'); for (const btn of allBtns) { const text = (btn.textContent || '').trim(); if ( text === 'Select Wallet' || text === 'Connect Wallet' || text === 'Connect' || text.startsWith('Connecting') || text === 'Change wallet' ) { return btn; } } // 再用 class 匹配 for (const sel of selectors) { const el = document.querySelector(sel); if (el) return el; } return null; } // ─── 注入拦截器 ─────────────────────────────────────────────────────────────── function injectInterceptor() { const btn = findConnectBtn(); if (btn && !btn.dataset.xicInjected) { // 克隆按钮移除原有监听器 const newBtn = btn.cloneNode(true); newBtn.dataset.xicInjected = 'true'; newBtn.addEventListener('click', (e) => { e.stopPropagation(); e.preventDefault(); openModal(); }); btn.parentNode.replaceChild(newBtn, btn); isInjected = true; console.log('[XIC Wallet] v2.1 多钱包连接器已注入'); } } // ─── 暴露全局 API ───────────────────────────────────────────────────────────── window.XICWallet = { open: openModal, close: closeModal, getAddress: () => currentAddress, getWallet: () => currentWallet, isConnected: () => !!currentAddress, disconnect: onDisconnected, }; // ─── 初始化 ─────────────────────────────────────────────────────────────────── function init() { // 使用 MutationObserver 持续监听 DOM const observer = new MutationObserver(() => { if (!isInjected) injectInterceptor(); }); if (document.body) { observer.observe(document.body, { childList: true, subtree: true }); injectInterceptor(); } else { document.addEventListener('DOMContentLoaded', () => { observer.observe(document.body, { childList: true, subtree: true }); injectInterceptor(); }); } // 多次重试确保注入成功(React 渲染后) [500, 1000, 1500, 2500, 4000].forEach(delay => { setTimeout(() => { if (!isInjected) injectInterceptor(); }, delay); }); } init(); })();