import React, { useState, useEffect, useRef } from 'react'; import { Shield, ShieldAlert, Network, CheckCircle, Search, Brain, Zap, Target, Activity, ChevronRight } from 'lucide-react'; import { ethers } from 'ethers'; import './Scanner.css'; import historicalTokensData from '../assets/historical_tokens.json'; import TokenDetailModal from './TokenDetailModal'; const RPC_NODES = [ "https://bsc-mainnet.public.blastapi.io", "https://bsc-rpc.publicnode.com", "https://bsc.drpc.org", "https://binance.llamarpc.com", "https://bsc-dataseed.binance.org/", "https://1rpc.io/bnb" ]; const SYSTEM_DOMAINS = [ 'flap.sh','binance.com','mypinata.cloud','w3.org','bscscan.com', 'warpcast.com','taxed.fun','debox.pro','allnodes.com','publicnode.com', 'flapdotsh', ]; const STANDARD_IMPLS = [ '0x024f18294970b5c76c0691b87f138a0317156422', '0x29e6383f0ce68507b5a72a53c2b118a118332aa8' ]; const formatTimeAgo = (unixSec) => { if (!unixSec) return 'Just now'; const diffSec = Math.max(0, Math.floor(Date.now() / 1000 - unixSec)); if (diffSec < 60) return `${diffSec}s ago`; const diffMin = Math.floor(diffSec / 60); if (diffMin < 60) return `${diffMin}m ago`; const diffHours = Math.floor(diffMin / 60); if (diffHours < 24) return `${diffHours}h ago`; return `${Math.floor(diffHours / 24)}d ago`; }; const implSeenCount = {}; const socialRegistry = {}; function seedSocialRegistry(historicalList) { if (Array.isArray(historicalList)) { historicalList.forEach(t => { const addr = t.tokenAddress?.toLowerCase(); if (!addr) return; const addKey = (url) => { if (!url) return; const key = url.trim().toLowerCase().split('?')[0]; if (key.includes('t.me/flap') || key.includes('x.com/flap') || key.includes('flap.sh') || key.length < 15) return; socialRegistry[key] = addr; }; addKey(t.tgUrl); addKey(t.xUrl); addKey(t.websiteUrl); }); } } seedSocialRegistry(historicalTokensData); let rpcIndex = 0; function getProvider() { return new ethers.JsonRpcProvider(RPC_NODES[rpcIndex]); } function rotateRpc() { rpcIndex = (rpcIndex + 1) % RPC_NODES.length; } async function safeCall(fn, retries = 3) { for (let i = 0; i < retries; i++) { try { const p = getProvider(); return await fn(p); } catch (err) { rotateRpc(); await new Promise(r => setTimeout(r, 1000)); } } return null; } const Scanner = () => { const [tokens, setTokens] = useState([]); const [isConnected, setIsConnected] = useState(false); const [selectedToken, setSelectedToken] = useState(null); const processedTokens = useRef(new Set()); const isPolling = useRef(false); const checkSocialClone = (url, tokenAddr) => { if (!url) return null; const key = url.trim().toLowerCase().split('?')[0]; if (key.includes('t.me/flap') || key.includes('x.com/flap') || key.includes('flap.sh') || key.length < 15) return null; const POPULAR_SOCIALS_BLACKLIST = [ 'pepecoineth', 'pepe.vip', 'shibtoken', 'shib.io', 'dogecoin', 'dogecoin.com', 'pancakeswap', 'babydogecoin', 'floki', 'floki.com', 'binance', 'cz_binance', 'vitalikbuterin', 'ethereum', 'solana', 'tether', 'usdt', 'circle', 'usdc', 'avax', 'avalanche', 'arbitrum', 'optimism', 'polygon', 'matic', 'uniswap', 'jupiterexchange', 'jup.ag', 'raydium', 'orca_so', 'bonk_inu', 'wif', 'dogwifhat' ]; if (POPULAR_SOCIALS_BLACKLIST.some(term => key.includes(term))) { return '0x_famous_project_copycat'; } if (socialRegistry[key] && socialRegistry[key] !== tokenAddr.toLowerCase()) { return socialRegistry[key]; } socialRegistry[key] = tokenAddr.toLowerCase(); return null; }; const enrichNewToken = async (rawToken) => { const addr = rawToken.tokenAddress; const tokenKey = addr.toLowerCase(); const trueDev = rawToken.creator || "0x0000000000000000000000000000000000000000"; // STRICT EXCLUSION: If tax > 8%, discard token before running further operations (max 8% + 1% Flap fee) if ((rawToken.buyTax && rawToken.buyTax > 8) || (rawToken.sellTax && rawToken.sellTax > 8)) { return; } const tgCloneOf = checkSocialClone(rawToken.tgUrl, addr); const xCloneOf = checkSocialClone(rawToken.xUrl, addr); const webCloneOf = checkSocialClone(rawToken.websiteUrl, addr); let hasSocialClone = !!(tgCloneOf || xCloneOf || webCloneOf); let cloneWarning = null; if (hasSocialClone) { cloneWarning = (tgCloneOf === '0x_famous_project_copycat' || xCloneOf === '0x_famous_project_copycat') ? `๐จ FAMOUS PROJECT CLONE (TRAP)!` : `๐จ Socials cloned from ${ (tgCloneOf || xCloneOf || webCloneOf).slice(0, 10) }...`; } const createdTimestamp = rawToken.createdTimestamp || Math.floor(Date.now() / 1000); let enrichedToken = { ...rawToken, createdTimestamp, taxInnovation: "Analyzing contract...", devClusterHistory: "Analyzing BscScan...", pastScamsCount: 0, funderText: "", hasSocialClone, cloneWarning, webStatus: rawToken.isStealth ? "No public socials" : (cloneWarning || "โ Verified socials"), timestamp: formatTimeAgo(createdTimestamp) }; setTokens(prev => { const map = new Map(); prev.forEach(t => map.set(t.tokenAddress.toLowerCase(), t)); if (!map.has(tokenKey)) { map.set(tokenKey, enrichedToken); } return Array.from(map.values()) .sort((a, b) => (b.createdTimestamp || 0) - (a.createdTimestamp || 0)) .slice(0, 100); }); // PHASE 3 ยท TAX IMPL let taxInnovation = "Standard (No Tax)"; if (rawToken.buyTax > 0 || rawToken.sellTax > 0) { taxInnovation = "Contract read unavailable"; const code = await safeCall(p => p.getCode(addr)); if (code && code.startsWith("0x363d3d373d3d3d363d73")) { const impl = "0x" + code.slice(22, 62).toLowerCase(); if (STANDARD_IMPLS.includes(impl)) { taxInnovation = `๐ Standard Flap Proxy (${impl.slice(0, 10)}...)`; } else { implSeenCount[impl] = (implSeenCount[impl] || 0) + 1; taxInnovation = `๐ Custom Flap Proxy (${impl.slice(0, 10)}...) โ Suspicious!`; } } else if (code && code.length > 10) { taxInnovation = "๐ Native Contract โ Clean Deployment"; } } // PHASE 4 ยท DEV CLUSTERING let devClusterHistory = "Dev analysis unavailable"; let funderText = ""; let pastScamsCount = 0; let bscScanSuccess = false; try { const scanRes = await fetch(`https://api.bscscan.com/api?module=account&action=txlist&address=${trueDev}&startblock=0&endblock=99999999&page=1&offset=2&sort=asc`); if (scanRes.ok) { const scanData = await scanRes.json(); if (scanData && scanData.status === "1" && scanData.result?.length > 0) { const funder = scanData.result[0].from?.toLowerCase() === trueDev.toLowerCase() ? scanData.result[0].to : scanData.result[0].from; const cnt = await safeCall(p => p.getTransactionCount(funder)); if (cnt !== null && cnt !== undefined) { if (cnt > 1000) { devClusterHistory = "โ Funder: Exchange/Bridge"; funderText = `${funder.slice(0, 10)}...`; } else if (cnt > 20) { devClusterHistory = `๐จ Suspicious funder (${cnt} txs)`; pastScamsCount = cnt; funderText = `Scam cluster: ${funder.slice(0, 10)}...`; } else { devClusterHistory = `โ Private wallet (${cnt} txs)`; funderText = `${funder.slice(0, 10)}...`; } bscScanSuccess = true; } } } } catch (_) {} if (!bscScanSuccess) { try { const cnt = await safeCall(p => p.getTransactionCount(trueDev)); if (cnt !== null && cnt !== undefined) { devClusterHistory = cnt > 10 ? `๐จ Serial deployer (${cnt} TX)` : `โ New deployer (${cnt} TX)`; pastScamsCount = cnt > 10 ? cnt : 0; } } catch (_) {} } setTokens(prev => { return prev.map(t => { if (t.tokenAddress?.toLowerCase() === tokenKey) { const webStatus = t.isStealth ? "No public socials" : (cloneWarning || "โ Verified socials"); return { ...t, taxInnovation, devClusterHistory, funderText, pastScamsCount, webStatus }; } return t; }).sort((a, b) => (b.createdTimestamp || 0) - (a.createdTimestamp || 0)); }); }; const fetchLiveLaunches = async () => { try { const res = await fetch('/api/gmgn/launches'); const data = await res.json(); if (data.success && Array.isArray(data.tokens)) { setIsConnected(true); for (const token of data.tokens) { const addrKey = token.tokenAddress?.toLowerCase(); if (!addrKey) continue; if (!processedTokens.current.has(addrKey)) { processedTokens.current.add(addrKey); enrichNewToken(token); } } } } catch (err) { console.error("Failed to load stream launches:", err); setIsConnected(false); } }; useEffect(() => { let isMounted = true; fetchLiveLaunches(); const interval = setInterval(async () => { if (isPolling.current || !isMounted) return; isPolling.current = true; await fetchLiveLaunches(); isPolling.current = false; }, 4000); return () => { isMounted = false; clearInterval(interval); }; }, []); const validTokens = tokens .filter(t => { const isBuyHigh = t.buyTax !== null && t.buyTax > 8; const isSellHigh = t.sellTax !== null && t.sellTax > 8; return !isBuyHigh && !isSellHigh; }) .sort((a, b) => (b.createdTimestamp || 0) - (a.createdTimestamp || 0)); return (
Listening for new Flap launches...