Greasy Fork 还支持 简体中文。

GMGN 前排数据统计

统计GMGN任意代币前排地址的数据,让数字来说话!

目前為 2025-07-19 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         GMGN 前排数据统计
// @namespace    http://tampermonkey.net/
// @version      1.6
// @description  统计GMGN任意代币前排地址的数据,让数字来说话!
// @match        https://gmgn.ai/*
// @match        https://www.gmgn.ai/*
// @grant        none
// @run-at       document-start
// @license MIT
// ==/UserScript==


/*
各位看官,如果脚本好用,可否点个关注?x:@tianxiamingxin
*/

(function() {
    'use strict';

    // 动态添加 CSS
    const style = document.createElement('style');
    style.textContent = `
        .gmgn-stats-container {
            background-color: #000;
            border-radius: 4px;
            font-family: Arial, sans-serif;
            margin-right: 10px;
        }
        .gmgn-stats-header, .gmgn-stats-data {
            display: grid;
            grid-template-columns: repeat(7, 1fr);
            text-align: center;
            gap: 8px;
            font-weight: normal;
        }
        .gmgn-stats-header span {
            color: #ccc;
            font-weight: normal;
        }
        .gmgn-stats-data span {
            color: #00ff00;
            font-weight: normal;
        }
    `;
    document.head.appendChild(style);

    // 存储拦截到的数据
    let interceptedData = null;

    // 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;
    };

    // 检测目标 API
    function isTargetApi(url) {
        return typeof url === 'string' &&
            /vas\/api\/v1\/token_holders\/sol(\/|$|\?)/i.test(url);
    }

    // 处理响应数据
    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)
                updateStatsDisplay();
            }).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 holders = interceptedData.data.list;
        const stats = {
            fullPosition: 0,    // 全仓
            profitable: 0,      // 盈利
            losing: 0,         // 亏损
            active24h: 0,      // 24h活跃
            diamondHands: 0,   // 钻石手
            newAddress: 0,     // 新地址
            highProfit: 0,     // 10x盈利
        };

        holders.forEach(holder => {
            if (holder.sell_amount_percentage === 0) 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.is_new) stats.newAddress++;
            if (holder.profit_change > 10) stats.highProfit++;
        });
        console.log('计算所有统计指标',stats)
        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('#gmgn-stats-item')) {
            injectStatsItem(targetContainer);
        }
    });



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

        const statsItem = document.createElement('div');
        statsItem.id = 'gmgn-stats-item';
        statsItem.className = 'gmgn-stats-container'; // 新增 CSS 类
        statsItem.innerHTML = `
        <div class="gmgn-stats-header">
            <span>满仓</span>
            <span>盈利</span>
            <span>亏损</span>
            <span>活跃</span>
            <span>钻石</span>
            <span>新址</span>
            <span>10x</span>
        </div>
        <div class="gmgn-stats-data">
            <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="highProfit">-</span>
        </div>
    `;
        container.insertAdjacentElement('afterbegin', statsItem);
    }



    // 3. 启动监听
    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
        });
    }


    // 5. 更新统计显示
    function updateStatsDisplay() {
        const stats = calculateStats();
        if (!stats) return;

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

        // 更新所有指标(绿色加粗)
        document.getElementById('fullPosition').innerHTML = `<strong style="color:green">${stats.fullPosition}</strong>`;
        document.getElementById('profitable').innerHTML = `<strong style="color: green">${stats.profitable}</strong>`;
        document.getElementById('losing').innerHTML = `<strong style="color: #ff5252">${stats.losing}</strong>`;
        document.getElementById('active24h').innerHTML = `<strong style="color: green">${stats.active24h}</strong>`;
        document.getElementById('diamondHands').innerHTML = `<strong style="color: green">${stats.diamondHands}</strong>`;
        document.getElementById('newAddress').innerHTML = `<strong style="color: green">${stats.newAddress}</strong>`;
        document.getElementById('highProfit').innerHTML = `<strong style="color: green">${stats.highProfit}</strong>`;
    }

    /*
// 白色系
'#ffffff'  // 纯白 (white)
'#f8f9fa'  // 浅白 (light white)
'#e9ecef'  // 灰白 (off-white)

// 绿色系(按亮度排序)
'#00ff00'  // 荧光绿 (图片同款)
'#00cc00'  // 亮绿
'#28a745'  // Bootstrap成功绿
'#228b22'  // 森林绿

// 红色系
'#ff0000'  // 纯红 (图片同款)
'#dc3545'  // Bootstrap危险红
'#c00000'  // 深红
'#ff4500'  // 橙红

// 其他常用数据颜色
'#ffa500'  // 橙色 (警告)
'#ffff00'  // 黄色 (注意)
'#007bff'  // 蓝色 (信息)
'#6f42c1'  // 紫色 (特殊标识)

*/

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



})();