GMGN 前排统计

统计GMGN任意代币前排地址的数据,让数字来说话!新增首次记录和涨跌提醒功能,所有数字可点击查看详情,弹框显示净流入数据,负数红色显示,点击外部关闭

当前为 2025-08-09 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         GMGN 前排统计
// @namespace    http://tampermonkey.net/
// @version      4.6
// @description  统计GMGN任意代币前排地址的数据,让数字来说话!新增首次记录和涨跌提醒功能,所有数字可点击查看详情,弹框显示净流入数据,负数红色显示,点击外部关闭
// @match        https://gmgn.ai/*
// @match        https://www.gmgn.ai/*
// @require      https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js
// @require      https://code.jquery.com/jquery-3.6.0.min.js
// @grant        none
// @run-at       document-start
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // 动态添加 CSS
    const style = document.createElement('style');
    style.textContent = `
    .statistic-gmgn-stats-container {
        background-color: transparent;
        border-radius: 4px;
        font-family: Arial, sans-serif;
        margin-right: 8px;
        margin-bottom:8px;
        border: 1px solid #333;
        /* 精细的右侧和下侧发光效果 */
        box-shadow:
            2px 2px 4px rgba(0, 119, 255, 0.6),  /* 右下外发光(更小的偏移和模糊) */
            1px 1px 2px rgba(0, 119, 255, 0.4),  /* 精细的次级发光 */
            inset 0 0 3px rgba(0, 119, 255, 0.2); /* 更细腻的内发光 */
        padding: 4px 6px;
        max-width: fit-content;
    }
    .statistic-gmgn-stats-header, .statistic-gmgn-stats-data {
        display: grid;
        grid-template-columns: repeat(9, 1fr);
        text-align: center;
        gap: 6px;
        font-weight: normal;
        font-size: 13px;
    }
    .statistic-gmgn-stats-header.sol-network, .statistic-gmgn-stats-data.sol-network {
        grid-template-columns: repeat(10, minmax(auto, 1fr));
        gap: 4px;
        font-size: 12px;
    }
    .statistic-gmgn-stats-header span {
        color: #ccc;
        font-weight: normal;
        padding: 1px 2px;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .statistic-gmgn-stats-header.sol-network span {
        font-size: 11px;
        padding: 1px;
    }
    .statistic-gmgn-stats-data span {
        color: #00ff00;
        font-weight: normal;
        cursor: default;
        transition: all 0.2s ease;
        padding: 1px 3px;
        border-radius: 2px;
        min-width: 0;
        white-space: nowrap;
    }
    .statistic-gmgn-stats-data span.clickable {
        cursor: pointer;
    }
    .statistic-gmgn-stats-data span.clickable:hover {
        background-color: rgba(0, 255, 0, 0.1);
        border-radius: 3px;
        transform: scale(1.03);
    }
    .statistic-gmgn-stats-data.sol-network span {
        padding: 1px 2px;
        font-size: 12px;
    }
    .statistic-gmgn-stats-data span .statistic-up-arrow,
    .statistic-up-arrow {
        color: green !important;
        margin-left: 2px;
        font-weight: bold;
    }
    .statistic-gmgn-stats-data span .statistic-down-arrow,
    .statistic-down-arrow {
        color: red !important;
        margin-left: 2px;
        font-weight: bold;
    }




    /* 完整弹框CSS样式 - 从token_holding_temp.js复制并添加statistic前缀 */
    .statistic-gmgn-modal {
                position: fixed;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                background-color: rgba(0, 0, 0, 0.5);
                display: flex;
                align-items: center;
                justify-content: center;
                z-index: 1000;
    }
    .statistic-gmgn-modal-content {
        background-color: #1e293b !important;
        border-radius: 8px !important;
        width: 80% !important;
        max-width: 800px !important;
        max-height: 80vh !important;
        overflow-y: auto !important;
        padding: 20px !important;
        color: white !important;
        position: fixed !important;
        top: 50% !important;
        left: 50% !important;
        transform: translate(-50%, -50%) !important;
        box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5) !important;
        margin: 0 !important;
        z-index: 100000 !important;
        box-sizing: border-box !important;
        min-height: auto !important;
        min-width: 300px !important;
        pointer-events: auto !important;
    }
    .statistic-gmgn-modal-header {
        display: flex !important;
        justify-content: space-between !important;
        align-items: center !important;
        margin-bottom: 16px !important;
        padding: 0 !important;
    }
    .statistic-gmgn-modal-title {
        font-size: 18px !important;
        font-weight: 600 !important;
        color: white !important;
        margin: 0 !important;
    }
    .statistic-gmgn-modal-close {
        background: none !important;
        border: none !important;
        color: #94a3b8 !important;
        font-size: 20px !important;
        cursor: pointer !important;
        padding: 5px !important;
        line-height: 1 !important;
        width: auto !important;
        height: auto !important;
        min-width: 30px !important;
        min-height: 30px !important;
    }
    .statistic-gmgn-modal-close:hover {
        color: #ff4444 !important;
        background-color: rgba(255, 255, 255, 0.1) !important;
        border-radius: 4px !important;
    }
    .statistic-gmgn-result-item {
        background-color: #334155;
        border-radius: 6px;
        padding: 12px;
        margin-bottom: 12px;
    }
    .statistic-gmgn-analysis-summary {
        margin-bottom: 16px;
        padding: 12px;
        background-color: #263238;
        border-radius: 6px;
        display: flex;
        justify-content: space-between;
        align-items: center;
    }
    .statistic-gmgn-summary-stats {
        display: flex;
        gap: 20px;
    }
    .statistic-gmgn-stat-item {
        display: flex;
        align-items: baseline;
    }
    .statistic-gmgn-stat-label {
        color: #94a3b8;
        margin-right: 5px;
    }
    .statistic-gmgn-stat-value {
        font-weight: 600;
        color: #3b82f6;
    }
    .statistic-gmgn-result-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 8px;
        flex-wrap: wrap;
        gap: 8px;
    }
    .statistic-gmgn-result-rank {
        font-size: 14px;
        color: #94a3b8;
        font-weight: 600;
        min-width: 30px;
    }
    .statistic-gmgn-result-address {
        font-weight: 600;
        word-break: break-all;
        cursor: pointer;
        padding: 4px 8px;
        border-radius: 4px;
        transition: all 0.2s ease;
        background-color: #475569;
        flex: 1;
        min-width: 200px;
        color: #00ff88;
        font-family: monospace;
    }
    .statistic-gmgn-result-address:hover {
        background-color: #64748b;
        transform: translateY(-1px);
    }
    .statistic-gmgn-detail-section {
        margin-bottom: 12px;
    }
    .statistic-gmgn-section-title {
        font-size: 13px;
        font-weight: 600;
        color: #94a3b8;
        margin-bottom: 8px;
    }
    .statistic-gmgn-detail-grid {
        display: grid;
        grid-template-columns: 80px 1fr 80px 1fr;
        gap: 4px 8px;
        align-items: start;
        font-size: 12px;
    }
    .statistic-gmgn-detail-label {
        color: #94a3b8;
        font-size: 12px;
        padding: 2px 0;
        align-self: start;
    }
    .statistic-gmgn-detail-value {
        font-size: 12px;
        color: #e2e8f0;
        padding: 2px 0;
        word-break: break-word;
        line-height: 1.4;
    }
    .statistic-gmgn-value-highlight {
        color: #3b82f6;
        font-weight: 600;
    }
    .statistic-gmgn-compact-details .statistic-gmgn-detail-section {
        margin-bottom: 8px;
    }
    .statistic-gmgn-compact-details .statistic-gmgn-detail-section {
        margin-left: 10px;
    }
    .statistic-gmgn-address-jump-btn {
        background-color: #10b981;
        color: white;
        padding: 4px 8px;
        border-radius: 6px;
        font-size: 11px;
        font-weight: 500;
        margin-left: 8px;
        cursor: pointer;
        transition: all 0.2s ease;
        text-decoration: none;
        display: inline-block;
        border: none;
    }
    .statistic-gmgn-address-jump-btn:hover {
        background-color: #059669;
        transform: translateY(-1px);
        box-shadow: 0 2px 4px rgba(16, 185, 129, 0.3);
    }

    .statistic-gmgn-profit-positive {
        color: #00ff88 !important;
    }

    .statistic-gmgn-profit-negative {
        color: #ff4444 !important;
    }

    .statistic-gmgn-empty-message {
        text-align: center;
        color: #ccc;
        padding: 20px;
        margin: 0;
    }

    .statistic-gmgn-stats-info {
        text-align: center !important;
        margin-bottom: 15px !important;
        padding: 10px !important;
        background: rgba(0, 119, 255, 0.1) !important;
        border-radius: 8px !important;
        border: 1px solid rgba(0, 119, 255, 0.3) !important;
        color: #fff !important;
        font-size: 14px !important;
    }

    .statistic-gmgn-export-btn {
        background-color: #10b981 !important;
        color: white !important;
        border: none !important;
        padding: 8px 16px !important;
        border-radius: 6px !important;
        font-size: 12px !important;
        font-weight: 500 !important;
        cursor: pointer !important;
        transition: all 0.2s ease !important;
        display: flex !important;
        align-items: center !important;
        gap: 4px !important;
    }

    .statistic-gmgn-export-btn:hover {
        background-color: #059669 !important;
        transform: translateY(-1px) !important;
        box-shadow: 0 2px 4px rgba(16, 185, 129, 0.3) !important;
    }
`;
    document.head.appendChild(style);

    // 存储拦截到的数据
    let interceptedData = null;
    // 存储首次加载的数据
    let initialStats = null;
    // 标记是否是首次加载
    let isFirstLoad = true;
    // 新增存储当前CA地址
    let currentCaAddress = null;
    // 存储首次加载的CA地址
    let initialCaAddress = null;

    // 检查当前网络是否为SOL
    function isSolNetwork() {
        const url = window.location.href;
        return url.includes('/sol/') || url.includes('gmgn.ai/sol');
    }
    // 优化后的弹框管理函数
    function createModal(title, data, caAddress, showSolBalance = false) {
        // 移除已存在的弹框
        const existingModal = document.querySelector('.statistic-gmgn-modal');
        if (existingModal) {
            existingModal.remove();
        }

        // 1. 数据预处理和排序 - 按收益率排序
        const processedData = data
            .sort((a, b) => (b.profit_change || 0) - (a.profit_change || 0)) // 按收益率排序
            .map((holder, index) => {
                const baseData = {
                    rank: index + 1,
                    address: holder.address,
                    balance: formatNumber(holder.balance),
                    usdValue: formatNumber(holder.usd_value),
                    netflowUsd: formatNumber(holder.netflow_usd),
                    netflowClass: (holder.netflow_usd || 0) >= 0 ? 'statistic-gmgn-profit-positive' : 'statistic-gmgn-profit-negative',
                    profit: formatNumber(holder.profit),
                    profitSign: holder.profit >= 0 ? '+' : '',
                    profitClass: holder.profit >= 0 ? 'statistic-gmgn-profit-positive' : 'statistic-gmgn-profit-negative',
                    profitChange: holder.profit_change ? (holder.profit_change * 100).toFixed(1) + '%' : 'N/A',
                    profitChangeClass: (holder.profit_change || 0) >= 0 ? 'statistic-gmgn-profit-positive' : 'statistic-gmgn-profit-negative'
                };

                // 只有在需要显示SOL余额时才添加
                if (showSolBalance) {
                    baseData.solBalance = holder.native_balance ? ((holder.native_balance / 1000000000).toFixed(2) + ' SOL') : 'N/A';
                }

                return baseData;
            });

        // 2. 创建弹框基础结构 - 使用token_holding_temp.js的DOM结构
        const modal = document.createElement('div');
        modal.className = 'statistic-gmgn-modal';
        modal.innerHTML = `
            <div class="statistic-gmgn-modal-content">
                <div class="statistic-gmgn-modal-header">
                    <div class="statistic-gmgn-modal-title">📊 ${title} (${processedData.length}个地址)</div>
                    <button class="statistic-gmgn-modal-close">&times;</button>
                </div>
                <div class="statistic-gmgn-analysis-summary">
                    <div class="statistic-gmgn-summary-stats">
                        <div class="statistic-gmgn-stat-item">
                            <span class="statistic-gmgn-stat-label">CA地址:</span>
                            <span class="statistic-gmgn-stat-value">${caAddress || 'N/A'}</span>
                        </div>
                        <div class="statistic-gmgn-stat-item">
                            <span class="statistic-gmgn-stat-label">总数量:</span>
                            <span class="statistic-gmgn-stat-value">${processedData.length}</span>
                        </div>
                    </div>
                    <button id="statistic-export-excel-btn" class="statistic-gmgn-export-btn" title="导出Excel">📊 导出Excel</button>
                </div>
                <div id="statistic-gmgn-results-list"></div>
                </div>
        `;

        // 3. 插入DOM
        document.body.appendChild(modal);

        // 4. 填充结果列表 - 参考token_holding_temp.js的方式
        const resultsList = document.getElementById('statistic-gmgn-results-list');
        processedData.forEach((holder, index) => {
            const item = document.createElement('div');
            item.className = 'statistic-gmgn-result-item';
            item.innerHTML = `
                <div class="statistic-gmgn-result-header">
                    <div class="statistic-gmgn-result-rank">#${holder.rank}</div>
                    <div class="statistic-gmgn-result-address" title="点击复制地址">${holder.address}</div>
                    <a href="https://gmgn.ai/sol/address/${holder.address}" target="_blank" class="statistic-gmgn-address-jump-btn" title="查看钱包详情">详情</a>
                                </div>

                <div class="statistic-gmgn-compact-details">
                    <div class="statistic-gmgn-detail-section">
                        <div class="statistic-gmgn-section-title">基本信息</div>
                        <div class="statistic-gmgn-detail-grid">
                            <span class="statistic-gmgn-detail-label">持仓:</span>
                            <span class="statistic-gmgn-detail-value">${holder.balance}</span>
                            <span class="statistic-gmgn-detail-label">净流入:</span>
                            <span class="statistic-gmgn-detail-value ${holder.netflowClass}">$${holder.netflowUsd}</span>
                            <span class="statistic-gmgn-detail-label">盈亏:</span>
                            <span class="statistic-gmgn-detail-value ${holder.profitClass}">${holder.profitSign}$${holder.profit}</span>
                            <span class="statistic-gmgn-detail-label">倍数:</span>
                            <span class="statistic-gmgn-detail-value ${holder.profitChangeClass}">${holder.profitChange}</span>
                            ${holder.solBalance ? `
                            <span class="statistic-gmgn-detail-label">SOL餘額:</span>
                            <span class="statistic-gmgn-detail-value statistic-gmgn-value-highlight">${holder.solBalance}</span>
                            ` : ''}
                            </div>
                </div>
            </div>
        `;

            // 添加地址复制功能
            const addressElement = item.querySelector('.statistic-gmgn-result-address');
            addressElement.addEventListener('click', () => {
                navigator.clipboard.writeText(holder.address).then(() => {
                    addressElement.style.backgroundColor = '#16a34a';
                    addressElement.style.color = 'white';
                    setTimeout(() => {
                        addressElement.style.backgroundColor = '';
                        addressElement.style.color = '';
                    }, 1000);
                });
            });

            resultsList.appendChild(item);
        });

        // ESC键关闭处理函数
        const escKeyHandler = (e) => {
            if (e.key === 'Escape') {
                document.body.removeChild(modal);
                document.removeEventListener('keydown', escKeyHandler);
            }
        };
        document.addEventListener('keydown', escKeyHandler);

        // 5. 绑定导出Excel按钮事件
        const exportBtn = modal.querySelector('#statistic-export-excel-btn');
        if (exportBtn) {
            exportBtn.addEventListener('click', () => {
                exportToExcel(processedData, title, caAddress, showSolBalance);
            });
        }

        // 6. 绑定关闭按钮事件
        modal.querySelector('.statistic-gmgn-modal-close').addEventListener('click', () => {
            document.body.removeChild(modal);
            document.removeEventListener('keydown', escKeyHandler);
        });

        // 点击模态框外部关闭
        modal.addEventListener('click', (e) => {
            if (e.target === modal) {
                document.body.removeChild(modal);
                document.removeEventListener('keydown', escKeyHandler);
            }
        });
    }





    // 数字格式化函数
    function formatNumber(num) {
        if (num === null || num === undefined) return 'N/A';

        // 處理負數:保留負號,對絕對值進行格式化
        const isNegative = num < 0;
        const absNum = Math.abs(num);

        let formatted;
        if (absNum >= 1000000000) {
            formatted = (absNum / 1000000000).toFixed(2) + 'B';
        } else if (absNum >= 1000000) {
            formatted = (absNum / 1000000).toFixed(2) + 'M';
        } else if (absNum >= 1000) {
            formatted = (absNum / 1000).toFixed(2) + 'K';
        } else {
            formatted = absNum.toFixed(2);
        }

        return isNegative ? '-' + formatted : formatted;
    }

    // Excel导出功能
    function exportToExcel(data, title, caAddress, showSolBalance) {
        try {
            // 创建工作表数据
            const worksheetData = [];

            // 添加标题行
            const headers = ['排名', '地址', '持仓数量', 'USD价值', '净流入USD', '盈亏USD', '盈亏倍数'];
            if (showSolBalance) {
                headers.push('SOL餘額');
            }
            worksheetData.push(headers);

            // 添加数据行
            data.forEach((holder, index) => {
                const row = [
                    holder.rank,
                    holder.address,
                    holder.balance,
                    holder.usdValue,
                    holder.netflowUsd,
                    (holder.profitSign || '') + holder.profit,
                    holder.profitChange
                ];

                if (showSolBalance) {
                    row.push(holder.solBalance || 'N/A');
                }

                worksheetData.push(row);
            });

            // 创建工作簿
            const wb = XLSX.utils.book_new();
            const ws = XLSX.utils.aoa_to_sheet(worksheetData);

            // 设置列宽
            const colWidths = [
                {wch: 6},   // 排名
                {wch: 45},  // 地址
                {wch: 15},  // 持仓数量
                {wch: 15},  // USD价值
                {wch: 15},  // 净流入
                {wch: 15},  // 盈亏
                {wch: 12}   // 倍数
            ];
            if (showSolBalance) {
                colWidths.push({wch: 12}); // SOL餘額
            }
            ws['!cols'] = colWidths;

            // 添加工作表到工作簿
            XLSX.utils.book_append_sheet(wb, ws, title);

            // 生成文件名
            const timestamp = new Date().toISOString().slice(0, 19).replace(/:/g, '-');
            const fileName = `${title}_${caAddress ? caAddress.slice(0, 8) : 'data'}_${timestamp}.xlsx`;

            // 下载文件
            XLSX.writeFile(wb, fileName);

            // 显示成功提示
            const exportBtn = document.querySelector('#statistic-export-excel-btn');
            if (exportBtn) {
                const originalText = exportBtn.textContent;
                exportBtn.textContent = '✅ 导出成功';
                exportBtn.style.backgroundColor = '#059669';
                setTimeout(() => {
                    exportBtn.textContent = originalText;
                    exportBtn.style.backgroundColor = '';
                }, 2000);
            }

        } catch (error) {
            console.error('Excel导出失败:', error);
            alert('导出失败,请检查浏览器控制台了解详情');
        }
    }

    // 根据类型获取对应的地址数据
    function getAddressByType(type) {
        if (!interceptedData?.data?.list) return [];

        const currentTime = Math.floor(Date.now() / 1000);
        const sevenDaysInSeconds = 7 * 24 * 60 * 60;
        const holders = interceptedData.data.list;

        switch(type) {
            case 'fullPosition':
                return holders.filter(h =>
                    h.sell_amount_percentage === 0 &&
                    (!h.token_transfer_out || !h.token_transfer_out.address)
                );
            case 'profitable':
                return holders.filter(h => h.profit > 0);
            case 'losing':
                return holders.filter(h => h.profit < 0);
            case 'active24h':
                return holders.filter(h => h.last_active_timestamp > currentTime - 86400);
            case 'diamondHands':
                return holders.filter(h => h.maker_token_tags?.includes('diamond_hands'));
            case 'newAddress':
                return holders.filter(h => h.tags?.includes('fresh_wallet'));
            case 'holdingLessThan7Days':
                return holders.filter(h =>
                    h.start_holding_at &&
                    (currentTime - h.start_holding_at) < sevenDaysInSeconds
                );
            case 'highProfit':
                return holders.filter(h => h.profit_change > 5);
            case 'suspicious':
                return holders.filter(h =>
                    h.is_suspicious ||
                    (h.maker_token_tags && (
                        h.maker_token_tags.includes('rat_trader') ||
                        h.maker_token_tags.includes('transfer_in')
                    ))
                );
            case 'lowSolBalance':
                return holders.filter(h =>
                    h.native_balance && (h.native_balance / 1000000000) < 1
                );
            default:
                return [];
        }
    }

    // 获取类型对应的中文标题
    function getTypeTitle(type) {
        const titles = {
            'fullPosition': '满仓地址',
            'profitable': '盈利地址',
            'losing': '亏损地址',
            'active24h': '24小时活跃地址',
            'diamondHands': '钻石手地址',
            'newAddress': '新地址',
            'holdingLessThan7Days': '持仓小于7天的地址',
            'highProfit': '5倍以上盈利地址',
            'suspicious': '可疑地址',
            'lowSolBalance': 'SOL餘額不足1的地址'
        };
        return titles[type] || '未知类型';
    }

    // 1. 拦截 fetch 请求
    const originalFetch = window.fetch;
    window.fetch = function(url, options) {
        if (isTargetApi(url)) {
            console.log('[拦截] fetch 请求:', url);
            return originalFetch.apply(this, arguments)
                .then(response => {
                if (response.ok) {
                    processResponse(response.clone());
                }
                return response;
            });
        }
        return originalFetch.apply(this, arguments);
    };

    // 2. 拦截 XMLHttpRequest
    const originalXHR = window.XMLHttpRequest;
    window.XMLHttpRequest = function() {
        const xhr = new originalXHR();
        const originalOpen = xhr.open;
        xhr.open = function(method, url) {
            if (isTargetApi(url)) {
                console.log('[拦截] XHR 请求:', url);
                const originalOnload = xhr.onload;
                xhr.onload = function() {
                    if (xhr.readyState === 4 && xhr.status === 200) {
                        processResponse(xhr.responseText);
                    }
                    originalOnload?.apply(this, arguments);
                };
            }
            return originalOpen.apply(this, arguments);
        };
        return xhr;
    };

    function isTargetApi(url) {
        if (typeof url !== 'string') return false;
        const isTarget = /vas\/api\/v1\/token_holders\/(sol|eth|base|bsc|tron)(\/|$|\?)/i.test(url);
        if (isTarget) {
            // 从URL中提取CA地址
            const match = url.match(/vas\/api\/v1\/token_holders\/sol\/([^/?]+)/i);
            console.log('匹配的ca:',match)
            if (match && match[1]) {
                currentCaAddress = match[1];
            }
        }
        return isTarget;
    }

    function processResponse(response) {
        console.log('开始处理响应数据');

        try {
            const dataPromise = typeof response === 'string' ?
                  Promise.resolve(JSON.parse(response)) :
            response.json();

            dataPromise.then(data => {
                interceptedData = data;
                console.log('[成功] 拦截到数据量:', data.data?.list?.length);
                console.log('[成功] 拦截到数据:',data);

                const currentStats = calculateStats();
                if (isFirstLoad) {
                    // 首次加载,记录初始数据和CA地址
                    initialStats = currentStats;
                    initialCaAddress = currentCaAddress;
                    isFirstLoad = false;
                    updateStatsDisplay(currentStats, true);
                } else {
                    // 非首次加载,比较CA地址
                    const isSameCa = currentCaAddress === initialCaAddress;
                    updateStatsDisplay(currentStats, !isSameCa);

                    // 如果CA地址不同,更新初始数据为当前数据
                    if (!isSameCa) {
                        initialStats = currentStats;
                        initialCaAddress = currentCaAddress;
                    }
                }
            }).catch(e => console.error('解析失败:', e));
        } catch (e) {
            console.error('处理响应错误:', e);
        }
    }


    // 3. 计算所有统计指标
    function calculateStats() {
        if (!interceptedData?.data?.list) return null;

        const currentTime = Math.floor(Date.now() / 1000);
        const sevenDaysInSeconds = 7 * 24 * 60 * 60; // 7天的秒数
        const holders = interceptedData.data.list;
        const stats = {
            fullPosition: 0,    // 全仓
            profitable: 0,      // 盈利
            losing: 0,         // 亏损
            active24h: 0,      // 24h活跃
            diamondHands: 0,   // 钻石手
            newAddress: 0,     // 新地址
            highProfit: 0,     // 10x盈利
            suspicious: 0,     // 新增:可疑地址
            holdingLessThan7Days: 0, // 新增:持仓小于7天
            lowSolBalance: 0   // 新增:SOL餘額小於1的地址
        };

        holders.forEach(holder => {
                // 满判断条件:1.没有卖出;2.没有出货地址
            if (holder.sell_amount_percentage === 0 &&
                (!holder.token_transfer_out || !holder.token_transfer_out.address)) {
                stats.fullPosition++;
            }
            if (holder.profit > 0) stats.profitable++;
            if (holder.profit < 0) stats.losing++;
            if (holder.last_active_timestamp > currentTime - 86400) stats.active24h++;
            if (holder.maker_token_tags?.includes('diamond_hands')) stats.diamondHands++;
            if (holder.tags?.includes('fresh_wallet')) stats.newAddress++;
            if (holder.profit_change > 5) stats.highProfit++;
            // 增强版可疑地址检测
            if (
                holder.is_suspicious ||
                (holder.maker_token_tags && (
                    holder.maker_token_tags.includes('rat_trader') ||
                    holder.maker_token_tags.includes('transfer_in')
                ))
            ) {
                stats.suspicious++;
            }
            // 新增7天持仓统计
            if (holder.start_holding_at &&
                (currentTime - holder.start_holding_at) < sevenDaysInSeconds) {
                stats.holdingLessThan7Days++;
            }
            // 新增低SOL餘額統計(小於1 SOL)
            if (holder.native_balance && (holder.native_balance / 1000000000) < 1) {
                stats.lowSolBalance++;
            }
        });
        return stats;
    }

    // 1. 持久化容器监听
    const observer = new MutationObserver(() => {
        const targetContainer = document.querySelector('.flex.overflow-x-auto.overflow-y-hidden.scroll-smooth.w-full');
        if (targetContainer && !targetContainer.querySelector('#statistic-gmgn-stats-item')) {
            injectStatsItem(targetContainer);
        }
    });

    function injectStatsItem(container) {
        if (container.querySelector('#statistic-gmgn-stats-item')) return;

        const isSol = isSolNetwork();
        const statsItem = document.createElement('div');
        statsItem.id = 'statistic-gmgn-stats-item';
        statsItem.className = 'statistic-gmgn-stats-container';

        const headerClass = isSol ? 'statistic-gmgn-stats-header sol-network' : 'statistic-gmgn-stats-header';
        const dataClass = isSol ? 'statistic-gmgn-stats-data sol-network' : 'statistic-gmgn-stats-data';

        statsItem.innerHTML = `
        <div class="${headerClass}">
    <span title="持有代币且未卖出任何数量的地址(排除转移代币卖出的地址)">满仓</span>
    <span title="当前持仓价值高于买入成本的地址">盈利</span>
    <span title="当前持仓价值低于买入成本的地址">亏损</span>
    <span title="过去24小时内有交易活动的地址">活跃</span>
    <span title="长期持有且很少卖出的地址">钻石</span>
    <span title="新钱包">新址</span>
    <span title="持仓时间小于7天的地址">7天</span>
    <span title="盈利超过5倍的地址">5X</span>
    <span title="标记为可疑或异常行为的地址">可疑</span>
    ${isSol ? '<span title="SOL餘額小於1的地址">低SOL</span>' : ''}
        </div>
        <div class="${dataClass}">
            <span id="fullPosition">-</span>
            <span id="profitable">-</span>
            <span id="losing">-</span>
            <span id="active24h">-</span>
            <span id="diamondHands">-</span>
            <span id="newAddress">-</span>
            <span id="holdingLessThan7Days">-</span>
            <span id="highProfit">-</span>
            <span id="suspicious">-</span>
            ${isSol ? '<span id="lowSolBalance">-</span>' : ''}
        </div>
    `;
        container.insertAdjacentElement('afterbegin', statsItem);
    }

    function updateStatsDisplay(currentStats, forceNoArrows) {
        if (!currentStats) return;

        // 确保DOM已存在
        if (!document.getElementById('statistic-gmgn-stats-item')) {
            injectStatsItem();
        }

        const updateStatElement = (id, value, hasChanged, isIncrease) => {
            const element = document.getElementById(id);
            if (!element) return;

            element.innerHTML = `<strong style="color: ${id === 'profitable' ? '#2E8B57' :
            (id === 'losing' || id === 'suspicious' ? '#FF1493' :
             id === 'holdingLessThan7Days' ? '#00E5EE' :
             id === 'lowSolBalance' ? '#FFA500' : '#e9ecef')}">${value}</strong>`;

            // 只有当不是强制不显示箭头且确实有变化时才显示箭头
            if (!forceNoArrows && hasChanged) {
                const arrow = document.createElement('span');
                arrow.className = isIncrease ? 'statistic-up-arrow' : 'statistic-down-arrow';
                arrow.textContent = isIncrease ? '▲' : '▼';

                // 移除旧的箭头(如果有)
                const oldArrow = element.querySelector('.statistic-up-arrow, .statistic-down-arrow');
                if (oldArrow) oldArrow.remove();

                element.appendChild(arrow);
            } else {
                // 没有变化或强制不显示箭头,移除箭头(如果有)
                const oldArrow = element.querySelector('.statistic-up-arrow, .statistic-down-arrow');
                if (oldArrow) oldArrow.remove();
            }

            // 为所有统计类型添加点击事件监听器
            const baseClickableTypes = ['fullPosition', 'profitable', 'losing', 'active24h', 'diamondHands', 'newAddress', 'holdingLessThan7Days', 'highProfit', 'suspicious'];
            const clickableTypes = isSolNetwork() ? [...baseClickableTypes, 'lowSolBalance'] : baseClickableTypes;

            if (clickableTypes.includes(id)) {
                element.classList.add('clickable');
                element.onclick = (e) => {
                    e.preventDefault();
                    e.stopPropagation();
                    const addresses = getAddressByType(id);
                    const title = getTypeTitle(id);
                    const showSolBalance = id === 'lowSolBalance';
                    createModal(title, addresses, currentCaAddress, showSolBalance);
                };
            } else {
                // 其他类型移除点击样式和事件
                element.classList.remove('clickable');
                element.onclick = null;
            }
        };
        // 更新各个统计指标
            // 新增7天持仓统计更新
        updateStatElement('holdingLessThan7Days', currentStats.holdingLessThan7Days,
                          initialStats && currentStats.holdingLessThan7Days !== initialStats.holdingLessThan7Days,
                          initialStats && currentStats.holdingLessThan7Days > initialStats.holdingLessThan7Days);

        updateStatElement('fullPosition', currentStats.fullPosition,
                          initialStats && currentStats.fullPosition !== initialStats.fullPosition,
                          initialStats && currentStats.fullPosition > initialStats.fullPosition);

        updateStatElement('profitable', currentStats.profitable,
                          initialStats && currentStats.profitable !== initialStats.profitable,
                          initialStats && currentStats.profitable > initialStats.profitable);
        updateStatElement('losing', currentStats.losing,
                          currentStats.losing !== initialStats.losing,
                          currentStats.losing > initialStats.losing);

        updateStatElement('active24h', currentStats.active24h,
                          currentStats.active24h !== initialStats.active24h,
                          currentStats.active24h > initialStats.active24h);

        updateStatElement('diamondHands', currentStats.diamondHands,
                          currentStats.diamondHands !== initialStats.diamondHands,
                          currentStats.diamondHands > initialStats.diamondHands);

        updateStatElement('newAddress', currentStats.newAddress,
                          currentStats.newAddress !== initialStats.newAddress,
                          currentStats.newAddress > initialStats.newAddress);

        updateStatElement('highProfit', currentStats.highProfit,
                          currentStats.highProfit !== initialStats.highProfit,
                          currentStats.highProfit > initialStats.highProfit);

        updateStatElement('suspicious', currentStats.suspicious,
                          currentStats.suspicious !== initialStats.suspicious,
                          currentStats.suspicious > initialStats.suspicious);

        // 只在SOL网络时更新低SOL余额统计
        if (isSolNetwork()) {
            updateStatElement('lowSolBalance', currentStats.lowSolBalance,
                              initialStats && currentStats.lowSolBalance !== initialStats.lowSolBalance,
                              initialStats && currentStats.lowSolBalance > initialStats.lowSolBalance);
        }
    }

    // 4. 初始化
    if (document.readyState === 'complete') {
        startObserving();
    } else {
        window.addEventListener('DOMContentLoaded', startObserving);
    }

    function startObserving() {
        // 立即检查一次
        const initialContainer = document.querySelector('.flex.overflow-x-auto.overflow-y-hidden.scroll-smooth.w-full');
        if (initialContainer) injectStatsItem(initialContainer);

        // 持续监听DOM变化
        observer.observe(document.body, {
            childList: true,
            subtree: true,
            attributes: false
        });
    }
})();