/**
* 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 ? '' : ''}