您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
统计GMGN任意代币前排地址的数据,让数字来说话!新增首次记录和涨跌提醒功能
当前为
// ==UserScript== // @name GMGN 前排数据统计 // @namespace http://tampermonkey.net/ // @version 2.5 // @description 统计GMGN任意代币前排地址的数据,让数字来说话!新增首次记录和涨跌提醒功能 // @match https://gmgn.ai/* // @match https://www.gmgn.ai/* // @grant none // @run-at document-start // @license MIT // ==/UserScript== (function() { 'use strict'; // 动态添加 CSS const style = document.createElement('style'); style.textContent = ` .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: 6px; } .gmgn-stats-header, .gmgn-stats-data { display: grid; grid-template-columns: repeat(9, 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; } .gmgn-stats-data span .up-arrow, .up-arrow { color: green !important; margin-left: 2px; font-weight: bold; } .gmgn-stats-data span .down-arrow, .down-arrow { color: red !important; margin-left: 2px; font-weight: bold; } `; document.head.appendChild(style); // 存储拦截到的数据 let interceptedData = null; // 存储首次加载的数据 let initialStats = null; // 标记是否是首次加载 let isFirstLoad = true; // 新增存储当前CA地址 let currentCaAddress = null; // 存储首次加载的CA地址 let initialCaAddress = 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; }; function isTargetApi(url) { if (typeof url !== 'string') return false; const isTarget = /vas\/api\/v1\/token_holders\/sol(\/|$|\?)/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天 }; 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.is_new) stats.newAddress++; if (holder.profit_change > 10) 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++; } }); 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'; statsItem.innerHTML = ` <div class="gmgn-stats-header"> <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="盈利超过10倍的地址">10X</span> <span title="标记为可疑或异常行为的地址">可疑</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="holdingLessThan7Days">-</span> <span id="highProfit">-</span> <span id="suspicious">-</span> </div> `; container.insertAdjacentElement('afterbegin', statsItem); } function updateStatsDisplay(currentStats, forceNoArrows) { if (!currentStats) return; // 确保DOM已存在 if (!document.getElementById('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' : '#e9ecef')}">${value}</strong>`; // 只有当不是强制不显示箭头且确实有变化时才显示箭头 if (!forceNoArrows && hasChanged) { const arrow = document.createElement('span'); arrow.className = isIncrease ? 'up-arrow' : 'down-arrow'; arrow.textContent = isIncrease ? '▲' : '▼'; // 移除旧的箭头(如果有) const oldArrow = element.querySelector('.up-arrow, .down-arrow'); if (oldArrow) oldArrow.remove(); element.appendChild(arrow); } else { // 没有变化或强制不显示箭头,移除箭头(如果有) const oldArrow = element.querySelector('.up-arrow, .down-arrow'); if (oldArrow) oldArrow.remove(); } }; // 更新各个统计指标 // 新增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); } /* // 白色系 '#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); } 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 }); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址