diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..b082b54 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,30 @@ +name: CI + +on: + push: + pull_request: + +jobs: + checks: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: Import smoke check + run: | + python -c "import debank_checker.api.client, debank_checker.checker, debank_checker.export.csv_exporter, debank_checker.export.excel, debank_checker.export.json_exporter, debank_checker.proxy.manager, debank_checker.ui.menu" + + - name: Bytecode compile + run: | + python -m compileall -q debank_checker main.py + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a439fb5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +# Python +__pycache__/ +*.py[cod] +*.pyo +*.pyd +.Python +*.so +.venv/ +venv/ +env/ + +# Output +Results/ +debug_log.txt + +# Local inputs (do not commit) +wallets.txt +proxy.txt + +# IDE +.cursor/ +.vscode/ +*.swp +*.swo + +# Tool logs +.playwright-mcp/ diff --git a/README.md b/README.md index f843d0e..d0aeae0 100644 --- a/README.md +++ b/README.md @@ -1,56 +1,124 @@ -# FarmerPro -Снимок экрана 2026-04-20 150801 +# DeBank Balance Checker -**[English](#english) | [Русский](#русский)** +[![CI](https://github.com/privatekey7/DeBankChecker/actions/workflows/ci.yml/badge.svg)](https://github.com/privatekey7/DeBankChecker/actions/workflows/ci.yml) + +CLI‑утилита на Python для массовой проверки кошельков через DeBank API (**Tokens + DeFi + NFT**) с экспортом результатов (**Excel / CSV / JSON**). + +**TG:** `https://t.me/privatekey_ai` --- -## English +## Возможности + +- **Массовая проверка** списка адресов из `wallets.txt` +- **Прокси‑ротация** + rate limiting / cooldown (для стабильности) +- **Параллельная обработка** кошельков +- **Экспорт**: + - Excel (`.xlsx`) + - CSV + - JSON +- **Фильтры экспорта** по сетям/токенам/NFT/протоколам (в меню после проверки) -A modular desktop tool for managing crypto accounts and wallets. +--- + +## Требования -| Module | Description | -|--------|-------------| -| EVM Balance Checker | Check balances of EVM wallets across networks | -| SVM Balance Checker | Check balances of Solana wallets | -| Token Collector | Collect tokens from wallets via swap and bridge | -| Proxy Checker | Check proxy availability and quality | -| Twitter Checker | Check Twitter/X account status | -| Discord Token Checker | Check Discord token status | +- **Python 3.10+** (рекомендуется) +- Windows / macOS / Linux -### Installation +Зависимости перечислены в `requirements.txt` (ключевые: `curl_cffi`, `openpyxl`, `questionary`, `colorama`). + +--- + +## Установка + +В папке проекта: ```bash -git clone https://github.com/privatekey7/Farmer_Pro.git -cd Farmer_Pro -pip install -r requirements.txt -python main.py +python -m venv .venv ``` -For detailed documentation visit: https://privatekey7.gitbook.io/farmerpro-en/ +Windows PowerShell: + +```powershell +.venv\Scripts\Activate.ps1 +``` + +Дальше: + +```bash +pip install -r requirements.txt +``` --- -## Русский +## Подготовка файлов + +### `wallets.txt` + +- **Один адрес на строку** +- Пустые строки игнорируются +- Строки, начинающиеся с `#`, считаются комментариями -Модульный десктопный инструмент для работы с крипто-аккаунтами и кошельками. +Пример: + +```text +# my wallets +0x0000000000000000000000000000000000000000 +0x1111111111111111111111111111111111111111 +``` -| Модуль | Описание | -|--------|----------| -| EVM Balance Checker | Проверка балансов EVM-кошельков по сетям | -| SVM Balance Checker | Проверка балансов Solana-кошельков | -| Token Collector | Сбор токенов с кошельков через свап и бридж | -| Proxy Checker | Проверка работоспособности прокси | -| Twitter Checker | Проверка статуса Twitter/X аккаунтов | -| Discord Token Checker | Проверка статуса Discord токенов | +### `proxy.txt` (обязательно) -### Установка +Прокси обязательны: при пустом `proxy.txt` программа завершится с ошибкой. + +Поддерживаемые форматы строк: + +- `ip:port` +- `ip:port:login:password` + +Пример: + +```text +127.0.0.1:8080 +10.10.10.10:3128:user:pass +``` + +--- + +## Запуск ```bash -git clone https://github.com/privatekey7/Farmer_Pro.git -cd Farmer_Pro -pip install -r requirements.txt python main.py ``` -Подробная документация: https://privatekey7.gitbook.io/farmerpro-ru/ +Поток работы: + +1. Запускается проверка всех кошельков +2. Печатается прогресс‑бар +3. После завершения открывается **интерактивное меню** экспорта (что именно выгружать) +4. Вы выбираете формат (**Excel / CSV / JSON**) +5. Файлы сохраняются в папку `Results/` + +--- + +## Результаты + +Вывод сохраняется в: + +- `Results/` + +Файлы именуются с учётом выбранного экспорта и времени запуска. + +--- + +## Защита от «фантомных» балансов + +Под высокой конкуренцией DeBank-API иногда возвращает портфель **другого адреса** +(response contamination): на кошельке с $15 «появляются» сотни тысяч/миллионы. +Воспроизводится только при параллельных запросах (последовательно — нет). + +Чекер принимает баланс кошелька, только если он **подтверждён двумя независимыми +запросами**, сошедшимися по сумме (корроборация). Случайный фантом не повторяется +и отбрасывается, истинный баланс стабилен и подтверждается. Параметры — в +`debank_checker/config.py` (`CORROBORATION_*`). diff --git a/app/__init__.py b/app/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/app/__main__.py b/app/__main__.py deleted file mode 100644 index c336f52..0000000 --- a/app/__main__.py +++ /dev/null @@ -1,2 +0,0 @@ -from app.main import main -main() diff --git a/app/core/__init__.py b/app/core/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/app/core/base_module.py b/app/core/base_module.py deleted file mode 100644 index f6f00bc..0000000 --- a/app/core/base_module.py +++ /dev/null @@ -1,45 +0,0 @@ -from __future__ import annotations -from abc import ABC, abstractmethod -from typing import AsyncIterator, TYPE_CHECKING - -if TYPE_CHECKING: - from PySide6.QtWidgets import QWidget - from .models import RunContext, Result, ColumnDef - - -class BaseModule(ABC): - """Базовый интерфейс для всех модулей FarmerPro.""" - - name: str # Отображаемое имя в боковом меню - - @abstractmethod - def get_config_widget(self) -> "QWidget | None": - """ - Возвращает Qt-виджет настроек этого модуля. - Возвращает None если у модуля нет настроек. - """ - - @abstractmethod - def run(self, ctx: "RunContext") -> "AsyncIterator[Result]": - """ - Возвращает async-генератор результатов. - TaskRunner итерирует: async for result in module.run(ctx). - """ - - @abstractmethod - async def stop(self) -> None: - """ - Устанавливает внутренний флаг отмены. - """ - - def get_item_count(self) -> int: - """Возвращает общее количество элементов для обработки. 0 = неизвестно.""" - return 0 - - def column_schema(self) -> "list[ColumnDef]": - """ - Defines which columns the ResultsTable should display, their order, - labels, formatting, and sort type. Return an empty list to fall back - to the legacy auto-derive behaviour (all result.data keys). - """ - return [] diff --git a/app/core/config.py b/app/core/config.py deleted file mode 100644 index 7c56516..0000000 --- a/app/core/config.py +++ /dev/null @@ -1,42 +0,0 @@ -from __future__ import annotations -import os -from pathlib import Path -from typing import Any -import yaml -from dotenv import load_dotenv -from .exceptions import ConfigError - -_DEFAULT_CONFIG = Path(__file__).parent.parent / "resources" / "config.yaml" - - -class Config: - _instance: Config | None = None - - def __init__(self, config_path: str | None = None): - load_dotenv() - path = Path(config_path) if config_path else _DEFAULT_CONFIG - if not path.exists(): - raise ConfigError(f"Config file not found: {path}") - with open(path, encoding="utf-8") as f: - self._data: dict[str, Any] = yaml.safe_load(f) or {} - Config._instance = self - - @classmethod - def instance(cls) -> "Config": - """Возвращает синглтон. Создаёт с дефолтным конфигом если ещё не инициализирован.""" - if cls._instance is None: - cls._instance = cls() - return cls._instance - - def get(self, key: str, default: Any = None) -> Any: - return self._data.get(key, default) - - def set(self, key: str, value: object) -> None: - """Обновляет значение в памяти и сохраняет config.yaml.""" - self._data[key] = value - with open(_DEFAULT_CONFIG, "w", encoding="utf-8") as f: - yaml.dump(self._data, f, allow_unicode=True, default_flow_style=False) - - def env(self, key: str, default: str | None = None) -> str | None: - """Читает переменную окружения.""" - return os.environ.get(key, default) diff --git a/app/core/exceptions.py b/app/core/exceptions.py deleted file mode 100644 index 6c6797d..0000000 --- a/app/core/exceptions.py +++ /dev/null @@ -1,17 +0,0 @@ -class FarmerProError(Exception): - """Базовое исключение проекта.""" - - -class RetryExhaustedError(FarmerProError): - """Все попытки retry исчерпаны.""" - def __init__(self, attempts: int, last_error: Exception): - super().__init__(f"Failed after {attempts} attempts: {last_error}") - self.last_error = last_error - - -class ConfigError(FarmerProError): - """Ошибка конфигурации.""" - - -class ParseError(FarmerProError): - """Ошибка парсинга входного файла.""" diff --git a/app/core/logger.py b/app/core/logger.py deleted file mode 100644 index 117bb0f..0000000 --- a/app/core/logger.py +++ /dev/null @@ -1,89 +0,0 @@ -from __future__ import annotations -import re -import logging -from datetime import datetime -from pathlib import Path -from typing import Any - -_PRIVATE_KEY_RE = re.compile(r"0x[0-9a-fA-F]{64}") -_LONG_TOKEN_RE = re.compile(r"\S{31,}") # строки >30 символов без пробелов - -_LOG_DIR = Path("logs") - - -def mask_sensitive(text: str) -> str: - """Маскирует приватные ключи и длинные токены в тексте.""" - text = _PRIVATE_KEY_RE.sub("0x***", text) - # Длинные токены (не URL и не hex-адреса, уже замаскированные) - def _mask_token(m: re.Match) -> str: - s = m.group(0) - # Пропускаем URL - if s.startswith("http") or s.startswith("0x***"): - return s - return "***" - text = _LONG_TOKEN_RE.sub(_mask_token, text) - return text - - -class _QtSignalHandler(logging.Handler): - """Маршрутизирует Python logging → Qt-сигнал UI виджета лога.""" - - def __init__(self, signal_fn: Any) -> None: - super().__init__(logging.INFO) - self._signal_fn = signal_fn - self.setFormatter(logging.Formatter( - "[%(asctime)s] %(levelname)-5s %(message)s", datefmt="%H:%M:%S" - )) - - def emit(self, record: logging.LogRecord) -> None: - try: - line = mask_sensitive(self.format(record)) - self._signal_fn(line) - except Exception: - pass - - -class Logger: - """Логгер с двумя sink'ами: файл и Qt-сигнал.""" - - def __init__(self, on_log_signal: Any = None, name: str = "farmerpro"): - self._signal = on_log_signal - self._log_dir = _LOG_DIR - self._log_dir.mkdir(exist_ok=True) - log_file = self._log_dir / f"{datetime.now().strftime('%Y-%m-%d')}.log" - self._file_logger = logging.getLogger(name) - if not self._file_logger.handlers: - handler = logging.FileHandler(log_file, encoding="utf-8") - handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s")) - self._file_logger.addHandler(handler) - self._file_logger.setLevel(logging.DEBUG) - - # Подключаем Python logging иерархию "app.*" к UI-сигналу - if self._signal is not None: - app_logger = logging.getLogger("app") - # Удаляем старые signal-обработчики чтобы не дублировать при повторном запуске - app_logger.handlers = [ - h for h in app_logger.handlers if not isinstance(h, _QtSignalHandler) - ] - app_logger.addHandler(_QtSignalHandler(self._signal.emit)) - app_logger.setLevel(logging.INFO) - - def _emit(self, level: str, message: str) -> None: - safe_msg = mask_sensitive(message) - timestamp = datetime.now().strftime("%H:%M:%S") - line = f"[{timestamp}] {level:5s} {safe_msg}" - # Файл - level_map = {"INFO": "info", "WARN": "warning", "ERROR": "error"} - getattr(self._file_logger, level_map.get(level, "info"))(safe_msg) - # UI сигнал - if self._signal is not None: - self._signal.emit(line) - - def info(self, message: str) -> None: - self._emit("INFO", message) - - def warning(self, message: str) -> None: - self._emit("WARN", message) - - def error(self, message: str) -> None: - self._emit("ERROR", message) diff --git a/app/core/models.py b/app/core/models.py deleted file mode 100644 index 52cabe0..0000000 --- a/app/core/models.py +++ /dev/null @@ -1,57 +0,0 @@ -from __future__ import annotations -from dataclasses import dataclass, field -from enum import Enum -from typing import Any - - -class ResultStatus(Enum): - OK = "ok" - ERROR = "error" - SKIP = "skip" - - -@dataclass -class ProxyConfig: - host: str - port: int - user: str | None = None - password: str | None = None - protocol: str = "http" # "http" | "socks5" - - def to_url(self) -> str: - """Возвращает прокси URL для httpx.""" - if self.user and self.password: - return f"{self.protocol}://{self.user}:{self.password}@{self.host}:{self.port}" - return f"{self.protocol}://{self.host}:{self.port}" - - -@dataclass -class RunContext: - items: list[str] - proxies: list[ProxyConfig] - rpc_urls: list[str] - concurrency: int - extra: dict[str, Any] = field(default_factory=dict) - - -@dataclass -class ColumnDef: - """Defines how a column is displayed in ResultsTable.""" - key: str # key in result.data (or "item"/"status") - label: str = "" # human-readable header; falls back to key - fmt: str = "" # Python format string, e.g. "${:.2f}", "{:.0f} ms" - width: int = 0 # hint width in px (0 = stretch) - sort_type: str = "text" # "text" | "numeric" - visible: bool = True # False = only in export, hidden from table - - def __post_init__(self) -> None: - if not self.label: - self.label = self.key - - -@dataclass -class Result: - item: str - status: ResultStatus - data: dict[str, Any] = field(default_factory=dict) - error: str | None = None diff --git a/app/core/module_registry.py b/app/core/module_registry.py deleted file mode 100644 index 937a9ad..0000000 --- a/app/core/module_registry.py +++ /dev/null @@ -1,24 +0,0 @@ -from __future__ import annotations -from .base_module import BaseModule - - -class ModuleRegistry: - """Реестр зарегистрированных модулей. Регистрация явная, в main.py.""" - - def __init__(self) -> None: - self._modules: list[BaseModule] = [] - - def register(self, module: BaseModule) -> None: - """Регистрирует модуль в реестре.""" - self._modules.append(module) - - def get_modules(self) -> list[BaseModule]: - """Возвращает все зарегистрированные модули в порядке регистрации.""" - return list(self._modules) - - def get_by_name(self, name: str) -> BaseModule | None: - """Ищет модуль по имени. Возвращает None если не найден.""" - for m in self._modules: - if m.name == name: - return m - return None diff --git a/app/core/retry.py b/app/core/retry.py deleted file mode 100644 index 7f5321f..0000000 --- a/app/core/retry.py +++ /dev/null @@ -1,35 +0,0 @@ -from __future__ import annotations -import asyncio -import functools -from typing import TypeVar, Callable, Awaitable -from .exceptions import RetryExhaustedError - -F = TypeVar("F", bound=Callable[..., Awaitable]) - -# Заменяемая для тестов функция sleep -_sleep = asyncio.sleep - - -def retry(attempts: int = 3, backoff: float = 2.0): - """ - Декоратор для async-функций с экспоненциальной паузой между попытками. - - Args: - attempts: максимальное количество попыток - backoff: множитель паузы (пауза = backoff ** attempt_index секунд) - """ - def decorator(fn: F) -> F: - @functools.wraps(fn) - async def wrapper(*args, **kwargs): - last_error: Exception | None = None - for attempt in range(attempts): - try: - return await fn(*args, **kwargs) - except Exception as e: - last_error = e - if attempt < attempts - 1: - delay = backoff ** attempt - await _sleep(delay) - raise RetryExhaustedError(attempts=attempts, last_error=last_error) - return wrapper # type: ignore - return decorator diff --git a/app/core/task_runner.py b/app/core/task_runner.py deleted file mode 100644 index e6acf2c..0000000 --- a/app/core/task_runner.py +++ /dev/null @@ -1,81 +0,0 @@ -from __future__ import annotations -import asyncio -from concurrent.futures import ThreadPoolExecutor -from typing import TYPE_CHECKING - -from PySide6.QtCore import QThread, Signal, QObject - -from app.core.models import Result, RunContext -from app.core.base_module import BaseModule - -if TYPE_CHECKING: - pass - - -class _Signals(QObject): - on_result = Signal(object) # Result - on_log = Signal(str) - on_finished = Signal() - - -class TaskRunner(QThread): - """ - Запускает модуль в отдельном потоке с собственным asyncio event loop. - Concurrency управляется через asyncio.Semaphore(ctx.concurrency). - """ - - def __init__(self, parent=None) -> None: - super().__init__(parent) - self._signals = _Signals() - self.on_result = self._signals.on_result - self.on_log = self._signals.on_log - self.on_finished = self._signals.on_finished - - self._module: BaseModule | None = None - self._ctx: RunContext | None = None - self._loop: asyncio.AbstractEventLoop | None = None - self._executor = ThreadPoolExecutor(max_workers=20) - - def submit(self, module: BaseModule, ctx: RunContext) -> None: - """Запускает модуль. Вызывать из UI-потока.""" - if self.isRunning(): - # Останавливаем предыдущий run и ждём завершения потока - if self._loop and self._module: - asyncio.run_coroutine_threadsafe(self._do_stop(), self._loop) - self.wait(5000) # ждём до 5 сек - - self._module = module - self._ctx = ctx - self.start() - - def stop_module(self) -> None: - """Останавливает текущий модуль. Вызывать из UI-потока.""" - if self._loop and self._module: - asyncio.run_coroutine_threadsafe(self._do_stop(), self._loop) - - async def _do_stop(self) -> None: - if self._module: - await self._module.stop() - - def run(self) -> None: - """Точка входа QThread. Создаёт asyncio loop и запускает задачи.""" - self._loop = asyncio.new_event_loop() - asyncio.set_event_loop(self._loop) - try: - self._loop.run_until_complete(self._run_module()) - finally: - self._loop.close() - self.on_finished.emit() - - async def _run_module(self) -> None: - if self._module is None or self._ctx is None: - return - semaphore = asyncio.Semaphore(self._ctx.concurrency) - self._ctx.extra["semaphore"] = semaphore - try: - async for result in self._module.run(self._ctx): - self.on_result.emit(result) - except asyncio.CancelledError: - pass - except Exception as e: - self.on_log.emit(f"[ERROR] Critical: {e}") diff --git a/app/data/solana_tokens.json b/app/data/solana_tokens.json deleted file mode 100644 index b1ef7fa..0000000 --- a/app/data/solana_tokens.json +++ /dev/null @@ -1,5754 +0,0 @@ -{ - "CVU6QRwpHz94UGyPFFehm1G1sFYRH7xDk9UhZ9RApump": "000", - "F9JkmcR9oXaV5kZJKbz75ZqiYRVnAwgcBJXj8JRzpump": "1234567891", - "CLD7wRUSwM68q51ayc1wt4Yipc6b2fwLqVm7Rv4Dpump": "035HP!", - "oxynABVJXUEc9PLNx4m1XVdiFtRhStzRXG1G6qosni1": "0XY", - "EaK5BoVLiGovVu1JNXJDguMtQyMid3zBX4xWVqhZpump": "100X", - "DpeJ6CkihBJzSNvgMcB9e56NT6Qiu2HL3nAGwYWUJESt": "FIGS", - "eLER748CTmFwiBiuU7nkz8iZQcaeV1KYc6AwZUipump": "VALENTINES", - "6UmWzmUy1RFa4eRGqpCL9Rat7oY4xXNV5Lt6pa84pump": "19", - "GMvCfcZg8YvkkQmwDaAzCtHDrrEtgE74nQpQ7xNabonk": "1-COIN-CAN-CHANGE-YOUR-LIFE", - "Yd8w2KJQ7BXe6JJTxqae9eRfSXzK15poXAvRwcYpump": "COMMUNITY", - "4R3DPW4BY97kJRfv8J5wgTtbDpoXpRv92W957tXMpump": "1DEV", - "8erRtd8mLgNcNoWy2bhMdTgZyXaRR6u7t2hyXRc1pump": "1DOG", - "4UTEFQjNMvfQF5NT8mVfXdMAKoL7hS7i9U4mMVAzpump": "$1", - "1HubD1oE9D8L5FQuSBs8r8NTgL3BgMqK1nkDx6Bjch4": "1HUB", - "Aih3sbAbu39Yn7jB2Qf4btZ5eWtDGQJH2gMfC4qdBAGS": "1LY", - "85ymfLLfeyYA9wV1PorpLBW1pprWfPgnm3cMEx5ppump": "MEME", - "Aea8zJW7jp1wkct3BjMeekBC1RQnHQyrvNutigc3pump": "CIRCLES", - "8tLbMZWuwB9oyXaJdAuPpL4ecVTdiNqGGR9n8m3bpump": "NARRATIVE", - "NJxAmP4tJZyRVaU5i2jG2ZmfguXzDjCQ67ZqRyBh111": "ONE", - "3dwu2tw7kBFZvWEdJMbPCGm7MBwgziABChLV1kGspump": "1%", - "bGxHNbsacaVL35pkYWae5PYQDZXSpuQb3QDyW31pump": "PEANUT", - "2LoiyomEa13iWFXdFMPMgy9W1w17JusdYKgzQb4D1to1": "1TO1", - "2GhzrpfpdoyttRwguGD38bWMvbTaCtL6ohjmv9J6pump": "YEAR", - "EwEJ5R1im54MowvsGwYUkyfWRMV1qcRL21PumVd4pump": "2016", - "J5ELogmzrdKiuSZxvKSKtfs8JRXMoR7YQEBHebr7pump": "2025", - "CQ1eU5VLsw2WyaDgVJPDBb1RyroeZoJaMeRaNkwtpump": "2026", - "HNi43Ue8Qz8XegA9q8AWbV9DxzJxcexm8czE1fhppump": "2077", - "4vGHdzcNrDf8XVE8H19Rqea86RULz7xi89ew1sSJpump": "21", - "4wDBLK2SzKRDH1SQgJkLQ9eEBggpBjYyLPMeoj6vPumP": "23", - "FA532HAN5BVwNsKMxXxj8gBWQj8QL2p8x8zNn3Mipump": "GOLDPEPE", - "7XJiwLDrjzxDYdZipnJXzpr1iDTmK55XixSFAa7JgNEL": "MLG", - "375aiUqZERLrB9sHtsArKr4nuWCgBrtqibUDBMGeLvZC": "EAT", - "GL3wQ5ADspXbAG3E8hUfSkWY884jSKfgFS6Fk9Npump": "39A", - "G63pAYWkZd71Jdy83bbdvs6HMQxaYVWy5jsS1hK3pump": "3EYES", - "Cz7LGKdZPpAxonXx23ZYPW3RtDQvjcf17ZDCZEzFpump": "401JK", - "ACyP5VHmSYhixsHwFXh8BTqypPGBTjHGcJZjYdBvPKaE": "401K", - "5CxtvaR1SskwLxfzHGurx8Enu8bgSTPyWF3YP4sWpump": "42069COIN", - "EUfRMccYw5E8Jv7NFmcpgKVFABqTr3QVputRQrbZpump": "4.25", - "5X742twtV5sf8ZnSWSZ3WhxHgR9wmVeQmvzpnK7Gpump": "DOPAMINE", - "42c846hyuCL5vMfqWifNvddUqHSqAFeJX7rDQBEBpKmM": "42", - "Pj3sX83x2gsxwwfzhUuszdA8EWcJvkJ4g3k3LCfpump": "4547", - "3dhk95qAjdJVQgBdiWUY9uCytWqukRUJ7RAgpG8Emoon": "4TOOL", - "HwPtbFpd3VTe3tfyosoVtPf9WPuSk5gAKkN5xp6Npump": "🟥🟩", - "b37NMA81bSgVmC6E5TXyrUndRzwdYycxoLFsG3gpump": "PAPER", - "FYdt8T5GXucuM8rTm72jNEk2iSpi74EcsmDKYQhppump": "501", - "9qriMjPPAJTMCtfQnz7Mo9BsV2jAWTr2ff7yc3JWpump": "\" \"", - "76rTxzztXjJe7AUaBi7jQ5J61MFgpQgB4Cc934sWbonk": "67", - "4sxxEHW6XqX5YBYs29f1p2RhR7afXFS8wQWcMYQVpump": "POP", - "EhNEwXZq89viTNhADmm5CT8GGHxF27694mzMUVjrpump": "6IXROOMS", - "J2bUGZDxRDpsVfjZqKwn6yYCUKFmqzHgt8UajhGtpump": "∅", - "FeKmTunVrXDKEoDJbuTwZi8vfFFw3MHzpPB79JD8ARYU": "FIW", - "5puhwnyz2Tv8jSmmBD5DSqCwFVXwwPGZacymM7DQpump": "8008", - "BzzPPSVod9sckAa4hSgpRFsTWZkQzYmJagRywpaUbonk": "發發發", - "888R77WmcLJKyGeJjk1WktFAB5u5fkvmokHYsAu6Spyd": "888", - "3xvLSHrLcM7246X1vu34cM9gNX741kQrzqj6T2HhLvXp": "COIN", - "9fcfqnpSW3ifkZeaRS19uHeovDRBLy57V89zh2rtpump": "8CHAN", - "8ZEfp4PkEMoGFgphvxKJrDySfS3T73DBfxKCdAsPpump": "8", - "2fNexKk1XFVRqRmuak4hFcgfpTSJf3HE6BH6f6bjkxz7": "9TO5", - "2QQVZpiCicFZM7wKHRvBtnvTxVBLxMtD1SJWbKSLpump": "A16G", - "HuiVprCHCucHUb5bX6EXFJd7wuwvdASFzzge4ahXpump": "TILLY", - "ENoD8J2J6wNHkcJkvVBkwq5JMiR1oNBfBZRkoHCQogyT": "AABL", - "129gRoHKhVg7CvPMrqVsEB4uYZo6zV4yDZX6NBg9ondo": "ABTON", - "XsHtf5RpxsQ7jeJ9ivNewouZKJHbPxhPoEy6yYvULr7": "ABTX", - "MFerpBVGKZh2jXN7cbJdXRXQTp6j6pbSnSZrfWrondo": "ABBVON", - "XswbinNKyPmzTa5CskMbCPvMW6G5CMnZXZEeQSSQoie": "ABBVX", - "F2w7z5sVe72SXnkj5qc48KXgXY2Pzj1fNtJXarmGpump": "ABI", - "P7hTXnKk2d2DyqWnefp5BSroE1qjjKpKxg9SxQqondo": "PALLON", - "XsTTtPA5V19YwHKDv4xeVXNM6kdsQNJvg3MyWkRUckt": "PALLX", - "DwRtkbsaQMGAS3oMeEGYh6M5vH4X9WECsQgqHjAondo": "PPLTON", - "Xst6eFD4YT6sz9RLMysN9SyvaZWtraSdVJQGu5ZkAme": "PPLTX", - "CgnZbDNzBfaLyJqUtd4esKLShRp7RznQuwP4uQaondo": "GLTRON", - "HiNkp9CdKqTgPtB6WnnrrUAu9YwQqnrvT6Ceuxoypump": "ACCELERANDO", - "12LxMMJYVSf4LoeqjFE47BQQNRciaH9E3nbDfjH4ondo": "ACNON", - "Xs5UJzmCRQ8DWZjskExdSQDnbE6iLkRu2jjrRAB1JSU": "ACNX", - "5MAYDfq5yxtudAhtfyuMBuHZjgAbaS9tbEyEQYAhDS5y": "ACS", - "GEuuznWpn6iuQAJxLKQDVGXPtrqXHNWTk3gZqqvJpump": "$ACE", - "GnHpRsrcyfHSMZNzmpjAzTFQA26vnbRMzbKQ11ZKpump": "ACE", - "4rUfhWTRpjD1ECGjw1UReVhA8G63CrATuoFLRVRkkqhs": "ACHI", - "HvQKzBzp1YhXdqDUrUB8fqrfbJ6rU9CytzscMyG7EFAe": "ACHI", - "YN4U8xySzuyARUMTNCpMgkkek7nnh2VAkUMdMygpump": "ACID", - "7Z1SttDgWt9UtcmSPnM9dmXztuiQS7oJS7NaYpJobonk": "COIN", - "EajMGPYVMzqUjXwnmyagVGDS2CW5xJitb17TpB4uDifD": "ACOLYT", - "8MxjSs3kEPmfiqKN1S4o3PABp198uYyx4gLF1TvZbonk": "ACT B", - "6ofzqLnHhobh7cJ4C63rAf5qnKU8AoMxVBi7Kkm6n9wq": "ACTP", - "7LSsEoJGhLeZzGvDofTdNg7M3JttxQqGWNLo6vWMpump": "FIGURE", - "GJAFwWjJ3vnTsrQVabjBVK2TYB1YtRCQXRDfDgUnpump": "ACT", - "E4Q5pLaEiejwEQHcM9GeYSQfMyGy8DJ4bPWgeYthn24v": "ADASOL", - "4rVf66MSzyjpLLVnGEuFMuVikEfa4XLbrQq2Zuexpump": "ADILLO", - "12Rh6JhfW4X5fKP16bbUdb4pcVCKDHFB48x8GG33ondo": "ADBEON", - "XsDZMGEU8zadWFCkTtPBoPWYcUX3JHVmghnwf2Mve2q": "ADBEX", - "AuQaustGiaqxRvj2gtCdrd22PBzTn8kM3kEPEkZCtuDw": "ADX", - "7h7FjNZGZ54KJzUtvx2eS9u61HbPX8XZS8WjyQtrpump": "ADZILLA", - "H8aWzESMpR8drxau9QYi95xPBdFZyFix1n5peFQKpump": "Æ", - "B16bhVYwE9zQVjbYpnnmvfN7qJFDe21nx5iQC5LJpump": "AEJO", - "CpEpquNgiGyeMeTBrJJCgMXL8vn1jCkWKVbSurhJpump": "AESOPERATOR", - "BEP6KhDwv7K211JtZnszNcztHBffSCgRjUuNhucopump": "AETHER", - "BYt1GhZfiWzynKc7WWo7y8kz38FRkyZyuypWUs6qc4Ls": "AIC", - "DGNicx6qMPKSL1deR3fZfbHYjnm5ZJWmHNdY2NhDpump": "AETH", - "Dm5BxyMetG3Aq5PaG1BrG7rBYqEMtnkjvPNMExfacVk7": "ATH", - "EsirN3orp85uyvZyDrZnbe9cyo7N1114ynLFdwMPCQce": "AEVUM", - "kMKX8hBaj3BTRBbeYix9c16EieBP5dih8DTSSwCpump": "AFK", - "2z5VijfstyHnDsvGwMExtvjTgTB1TT3JSf29PRe9MvNG": "AKIDS", - "6H7NbbGdn2Jo5qpJeHvVECnVMPgD32AfsjcDLK3zjfmt": "AH", - "DH4MqNmSR7rSY1zLiEaszx8cyDPCNgTWWTm4w8TMpump": "AGAWA", - "5yC9BM8KUsJTPbWPLfA2N8qH1s9V8DQ3Vcw1G6Jdpump": "AGENC", - "CN162nCPpq3DxPCyKLbAvEJeB1aCxsnVTEG4ZU8vpump": "A47", - "9g78Mv7JGsGCmnf7gJiEvVJNAn7fRLqjvJLMRMHbpump": "AGNS", - "BKgcrCw55yb1ocLS2MnQLT2dSkWtAPjxTanLoWB8oZso": "CLAWLETT", - "8k1eciW1AN1Bi6bdcpfh1qZn71oQTpNU31gdnMi2pump": "AAXN", - "BkprZMuBFxWNFz9zhj7qSxZvRndnVKkq5pPQ5sqcpump": "AGENTBANK", - "6V1vZKRFfu6eimDNk6w4BzxR4UTYxJvt5emuJkxTcMJv": "AIDOGE", - "CuXmQvh4DVTdWBdC2d3pNq8UXqbKJ3w9RPBTAALcKcTb": "DIRA", - "HUSTLFV3U5Km8u66rMQExh4nLy7unfKHedEXVK1WgSAG": "HUSTLE", - "yLUD35WTiPLEY6DUqEj5W2JVXF2DfKB5arPkKJXpump": "APY", - "8TC4ZJA5cwB5gmLemBfPP95v1Mft82kHGF9a87kepNno": "CVAI", - "9gwTegFJJErDpWJKjPfLr2g2zrE3nL1v5zpwbtsk3c6P": "USEDCAR", - "CNK4foXBWtyvNDyh5dk5DsNjsNouiFzdaKD4kHJdpump": "AGME", - "F86LPriiF5mnqpvaX2UqtKeJAjjSD12EAFoBg4JRpump": "AGENT", - "27yzfJSNvYLBjgSNbMyXMMUWzx6T9q4B9TP8Jt8MZ9mL": "ROGUE", - "Ag5mMdBSxm8J893RazD9PGkxWKJ5o3UKdWW4ZmJAey9w": "AGENT", - "GAMwtMB6onAvBNBQJCJFuxoaqfPH8uCQ2dewNMVVpump": "TANK", - "D8RaN4ZXKkemC7pyu2noMB8n6ptDPkzuMFp4KfZSpkin": "TED", - "EgGSqHYuysAEvtP1YxnjcBZNfucT7g4JWzdnURgKSXSr": "AWS", - "AeAYE1LytXEqX4L8SpLPmnHFePtwhAYSxex8f4gCmoon": "AGXBT", - "tWKHzXd5PRmxTF5cMfJkm2Ua3TcjwNNoSRUqx6Apump": "AGIALPHA", - "6ob7KUVQtXMpP1R9YcDWqoYsGKtYhUsSkMC4d7Vtpump": "AGI", - "8gUcEyfmuFvnK3byAzWALRaKSvgyzFbCwyU2eQ6tpump": "AGI", - "F9TgEJLLRUKDRF16HgjUCdJfJ5BK6ucyiW8uJxVPpump": "AGIXT", - "HC1sJQuYAdsjrPeKXeNkJGP9yPpdjpNtngUGhuKMpump": "AGOK", - "AUSD1jCcCyPLybk1YnvPWsHQSrZ46dxwoMniN4N2UEB9": "AUSD", - "AGAxefyrPTi63FGL2ukJUTBtLJStDpiXMdtLRWvzambv": "AGA", - "AGRidUXLeDij9CJprkZx7WBXtTQC67jtfiwz293mVrJ": "AGRI", - "CZkxnM5PNPak31JSFNzJ76CWcYRu4mgxvBwcHaBJpump": "AI4", - "HeLp6NuQkmYB4pYWo2zYs22mESHXPQYzXbB8n4V98jwC": "AI16Z", - "wUtwjNmjCP9TTTtoc5Xn5h5sZ2cYJm5w2w44b79yr2o": "ELIZA", - "CANKciNUFnuJfWPc4jxzrPyJaR7gBpuNSyRdd6ULpump": "GUDTEK", - "DKffyw1oEVWR3ik7q7tb2shhmZ3jbTkRzT6R5banCxai": "AI20X", - "DvyxUPDrDSLLziu5YsTkyirRDTncqkrWTsUdNCuipump": "AI69X", - "DS6qJNTGJUz26tjC6G75hGXGA6dvEyyiupS7okhLpump": "AI99X", - "7bD1RTKSZSsneDXDoMincSswED3uBnzuRyHi5tJ7ZjwN": "KAIA", - "sDLRYc96bWkJCVgRaR8FTvokw2iSg8bSQrHR7vHpump": "CARICATURE", - "6W34k5VBA68DXFZPfLz4o8yJgJoiiR4YoQfbtAAZpump": "AICAT", - "3zQ1XAcbSejFZNdbTBGvFGQatvViYbcwgXZ5pQ3KRRaw": "AICC", - "BkgPQZirJDceEp82JmguB7WFvomwqMwxdSSM9XfXpump": "AICOIN", - "217dt9idH1Q9UXT8rjpknnyFggizXUVD2r5EZNjopump": "AICOIN", - "99ouK5YUK3JPGCPX9joNtHsMU7NPpU7w91JN4kdQ97po": "AI", - "PD11M8MB8qQUAiWzyEK4JwfS8rt7Set6av6a5JYpump": "AICRYNODE", - "GbUA2PVzxsVmUNfWkjRSzut4kw4y7mit9S6VRLY2CYC": "CYC", - "AdJ7FFvw7Yo7LzKbcFuRpNqScLQr54FW9mE5ZatACvcJ": "AIDAOVC", - "AD8ra3bPosVujHQDofATvPciT1Q9xY4DLEgXbGzopump": "AIDEN", - "PLNk8NUTBeptajEX9GzZrxsYPJ1psnw62dPnWkGcyai": "AIDP", - "6Bp9LWtyVXcEATi5nuquX5p1hPGUg1cVoUuXGN9Epump": "AIFRUITS", - "9vtNLbN2qxPbrx666rP34s9PBGtyLrsupMGdVyqG7FzH": "$AIGCR", - "2fdtCHuvyLcD2q86XZGFmYbDux9ZbbUgMmFhzChqmoon": "OLIVIA", - "Bs7diZazUH2PgNyWh7qj3kgUFXqumZpkAjTGSzQKx4cV": "AIINU", - "Ey8GJs2xgXcGJXjanwzB7qcaHftCa7HwxFBNEHR9pump": "AIKA", - "2tgZJ6N7buMDq9HZWbzXvSPFq6MYWbrAGCoDD22Ypump": "AIKO", - "3He53vyEdBSD3Pxx5vAJ4AzQz3GXLXYBvKcYiTJ2pump": "AILIVE", - "haahyN2RTafHNNySeXKoowgGF7DdX5b5nGNGmghpump": "UAI", - "FVdo7CDJarhYoH6McyTFqx71EtzCPViinvdd1v86Qmy5": "AIMONICA", - "4c53VeEq8jVA9EoeGAz9tBC9hhNYHj5quwrMDqEiYxM3": "AIM", - "4GaCFbxuQ6db8RAepnvvMLvuZCsSmbjZoBwkEyfYLN9X": "AINARA", - "EiVMQrvdDYK77eK6N6cyh5sGBe5Y1MQWVA7YaURVpump": "AINTI", - "BAezfVmia8UYLt4rst6PCU4dvL2i2qHzqn4wGhytpNJW": "AINTI", - "5jpnC4Ty3dhbpjdVKihpF5tTpYN2XfH6Wmn67hbipump": "AIONIX", - "5QS7RcHfGUa2ZtrovPvEJMB9coqroiT7H48dPSwFpump": "AIOS", - "29BY5BHtLCG2mamRtC9hh9283U3QkbYLSCwXXmEJpump": "AIPETS", - "BAEXK4X6B3hkqmEkPuyyZQ5fZUb5iZ6SaJ7a9UDnpump": "AICMP", - "2w3A2P5juwg234spHKfps7WReWoVmujtErqjaZm9VaiP": "AIPUMP", - "2jvsWRkT17ofmv9pkW7ofqAFWSCNyJYdykJ7kPKbmoon": "AIR3", - "128qNYovdGv2YqayErcJgU7gDwbNVX1VuoxbtWz8ondo": "ABNBON", - "67icoxwiNAVLqagX4kG2cfT9mkSFkwP97kQtgHA7pump": "AIRCOIN", - "GpdRNotihwXZJ3z3y6yCRAhecFrALc4J8SX92pxnbonk": "AIRCOIN", - "61V8vBaqAGMpgDQi4JcAwo1dmBGHsyhzodcPqnEVpump": "ARC", - "4WRh23cmLWCwLgNnDd3V6CLCcMXFDgTQciB9tNejpump": "AIRI", - "B37KG7jHv1TkhJwe9xTuLuQyzTXvgQjAmBaxLeCrpump": "AISCII", - "G8mxEVehsoguE8xCYieHaS4r7hU5ioWkLkHYRgKdhdxn": "AISHARE", - "G5k8QhXrkJuBDvYokADrwvJ27wMWG3x8Pwy3qKCzTidu": "NOVA", - "g2M4twkhi5uGMdHQS8RbugciyABB2CmngqSUBDupump": "AISI", - "2wzVMXhLypmP92mXNCq4fuFcd9TCC972AbMfuiH3pump": "AISLOP", - "9ZFfZwZDfoSqj7HFD7BNGy57XVNkd1BR2UrNYKsnpump": "AISM", - "D4G7rpcQBF5oVQwBzoDwHPiweJ5RYvuEVGdxqmPApump": "AIT", - "4ptXaGHy4CcCBfr5SPQZK5bcH9N4njv4LCf1RaJqpump": "AIVIA", - "7JNdLsAgBMi2WZW26SeifhMNEt3LrLcfM8oBQVT7pump": "WARPER", - "2KgAN8nLAU74wjiyKi85m4ZT6Z9MtqrUTGfse8Xapump": "SHEGEN", - "14zP2ToQ79XWvc7FQpm4bRnp9d6Mp1rFfsUW3gpLcRX": "AIXBT", - "DRoPH9pb1eXQ3VvhwWuP1k5P5R9hkxVmniZCouihxH6w": "AIXDROP", - "AiXxRGmRc5oDiFXbEeRX9obPpr3Zir7rks1ef2NjddiF": "AIX", - "Akiox1GAxohWdggSLaFpChxLyS54vz7P7YaF1tckWEQu": "AKIO", - "GwXvPgGQDEhy71yxjghTKDRsPSgUFwf2s4wkD4Xgpump": "AKIRA", - "hUoehiMy279k95UeSijKkjx7RUsb676KSDgJ2i3xYbW": "AKT", - "3swKY5FN4ENQYYJTYqQzGbT7iEv8yD5UngCWfk3fEVA7": "ALAN", - "s3ghbbWBTtMizNJKGmeMBd1URpisoQJxuDLrheEX61g": "ALASKA", - "FEj9JuKq7JMLtvZF3EetVNd5ufU2vPA4ZDiVGaUbiRkj": "ALBEMARLE", - "B5KufqHkskgGYwMXtL8FSHgREAkMQvE3ykhH5Kmondo": "ALBON", - "HNg5PYJmtqcmzXrv6S9zP1CDKk5BgDuyFBxbvNApump": "ALCH", - "E5ndSkaB17Dm7CsD22dvcjfrYSDLCxFcMd6z8ddCk5wp": "RIN", - "3UCMiSnkcnkPE1pgQ5ggPCBv6dXgVUy16TmMUe1WpG9x": "ALEPH", - "9wvorGtBJ8gyLorFTmwXWcymPoGVUBn6MRzHwFpCdCeC": "ALI", - "6Xx8p2WmY1Uk2GD35uxhEyuniNrVEeSu3CUThb8Upump": "ALETHEIA", - "ATygpFdY1KUVn5YLB1CU6L9Xg5Z1FAY9qimtEae3wBLV": "ALG", - "4b83cYeo4BYfP9NfFUQQWRQYfomJjjChfsV64m5MqifY": "ALGOAI", - "1zvb9ELBFShBCWKEk5jRTJAaPAwtVt7quEXx1X4ondo": "BABAON", - "8T988C2a8YNNbppjjd1uYUpmDqE8hLee1tuNokuepump": "ALICE", - "HWrgYyGdivwuxV2ePxbk6vwkrKYqMa7is9fYMm5kcQWY": "ALICE", - "GfkfESc5A4EkxCGWsymNY4ZfkCDdvtB7ajCybLfYL1Qq": "AOC", - "GCSfrFVebzoJ9kK5jh8TGW8oJwWeN1n618F98ycLpump": "ALIENS", - "65YnRAiThkUda2czNFXVuMaCTiNpiq3LLLWbtGTLpump": "ALIENS", - "HPgsCBk6gkTw5FFPYc9WeeBU8EU2Ye1AYmeTYq5apump": "ALINAINTEL", - "F3nefJBcejYbtdREjui1T9DPh5dBgpkKq7u2GAAMXs5B": "AART", - "a11bdAAuV8iB2fu7X6AxAvDTo1QZ8FXB3kk5eecdasp": "ABR0", - "ADNScivK6Pt2XsfsufuyUiZaKbRww5JANDvZ7texDid1": "ADNS", - "HyDKNdnhZNVYQMruBevbNsUWruA9STmAQrS4srXApump": "ALLINDOGE", - "5EaYZcaKfTVdpQ2avVtJ7BNWJ1Rnj86F1dWxppawpump": "DOLLO", - "8ZeTmGGktvSwSSghx8btbTAVGdWogThKM4DQBJgRpump": "ROME", - "9pCWprxbZMW2rYSMhPTeoyLMV2sN2WJqshdT7DQVt6hS": "EURAU", - "Ai4CL1SAxVRigxQFwBH8S2JkuL7EqrdiGwTC7JpCpump": "AWR", - "3Sf6oKCeEqCuco4aYtKHHDTBYLAWHiL47QjvkW1UYDEG": "NOTHING", - "7gASrhXxE5u2oLASrAsZh3SAJrgSjKhb4qTe3FsqHTKb": "ALOKA", - "8XtRWb4uAAJFMP4QQhoYYCWR6XXb7ybcCdiqPwz9s5WS": "ALON", - "8ThSrRA6qpkva4bGE6DNfFKnuKuCPM22Db16zS1Mpump": "ALP", - "FfgtU4bMte3JRZGX7kcNVywxiGoSqUTga8eZxTSFpump": "ALPACA", - "8gJAaxa5YkrDpXRF6qw1fHW5nxM8sCUkQ9WfXasgpump": "ALPACO", - "2zrH2jE542mzB4HABgBjdWMQPtNC5H12pwo1iLpfpump": "ALPHA", - "2sCUCJdVkmyXp4dT8sFaA9LKgSMK4yDPi9zLHiwXpump": "ALPHA", - "Cg93SZJkHePybZqGDuyXLf5Ag5sB2cpWfHUG8wNPpump": "ALPHA", - "bbahNA5vT9WJeYft8tALrH1LXWffjwqVoUbqYa1ondo": "GOOGLON", - "XsCPL9dNWBMvFtTmwcCA5v3xWPSMEBCszbQdiLLq6aN": "GOOGLX", - "3wtGGWZ8wLWW6BqtC5rxmkHdqvM62atUcGTH4cw3pump": "ALPHA", - "CotWkXoBD3edLb6opEGHV9tb3pyKmeoWBLwdMJ8ZDimW": "AIKEK", - "FC5oim6UT5Zw1D5S5BZzMM7NQdzyuaSsGef9RT6Ppump": "ALPHAX", - "Dst1UGBW312B8FbxWuxyPjZ82ALrLvRq4zzS8MnMpump": "ALPAY", - "aqQRs3UJmHs7ktoLo46HZAzvG9P3oCoZ41nENqFbory": "PIE", - "FwzpNxnabjZvc8QCnV6qPEBKxqLxSyjobc5Etdgxpump": "ALR", - "Cdq1WR1d4i2hMrqKUWgZeUbRpkhamGHSvm1f6ATpuray": "ALT", - "12XvRbmvA3AY8La3brnQGcMR5HBp7Cn3Fxc5kKiQ86FY": "ALTSEASON", - "8m9fjYycXAFva1kScttQgsESVZT7yELhjZASqfHBuMa5": "JOWNES", - "Trj7guTUcpDFqkycDzvMNjwRN1W4fYtsxSYbj3veBsP": "AMATO", - "14Tqdo8V1FhzKsE3W2pFsZCzYPQxxupXRcqw9jv6ondo": "AMZNON", - "Xs3eBt7uRfJX8QUs4suhyU8p2M6DoUDrJyWBa8LLZsg": "AMZNX", - "XsaQTCgebC2KPbf27KUhdv5JFvHhQ4GDAPURwrEhAzb": "AMBRX", - "amb1bbMCnsnMgXfqj6JK359B8T23FijFgiLQiCrd9FM": "AMBIOS", - "9jaZhJM6nMHTo4hY9DGabQ1HNuUWhJtm7js1fmKMVpkN": "AMC", - "C9xNaNujcF1a5fidWAAFReFYqhLRVbyk4yPyGqzondo": "AMCON", - "14diAn5z8kjrKwSC8WLqvBqqe5YmihJhjxRxd8Z6ondo": "AMDON", - "XsXcJ6GZ9kVnjqGsjBnktRcuwMBmvKWh8S93RefZ1rF": "AMDX", - "kh35nynonqA4VYaoeAvn17ChrhMrWhJ26EbCayKpump": "AMELIAJAK", - "8TuHAfTBTMW1fJNq5bNoeX4zhk2492DNsCgVSYzApump": "AMELIA", - "2MwD89A8xuCpaiDqBsDEHe6rANhPou8PPKdTo7ETpump": "AMERICA", - "4SkFDZZ1SxSw3M79ohRPfmdxYKKCd21hxQnhYeWNpump": "AF", - "5GfXzzdxUZ4QEwwa5hhBMEazFSp9EhtYvm8RFLxrgyVc": "AIB", - "9wYZetvT8J2ptfsRca5gzLBGvcUug38mp9yT3xaondo": "AALON", - "69kdRLyP5DTRkpHraaSZAQbWmAwzF9guKjZfzMXzcbAs": "USA", - "1WxT6NdK7uqpfXuKpALxL2n3f7Rq61XXeHA8UM4ondo": "AXPON", - "XstYR6KGqZFchAuhHoEe4mL8W2kL9aqGW1EDBKRrmGB": "AXPX", - "7ea3ifsARjj2TeuPgScj5fNENpVodU4vXrA8yQcH8FK2": "AI", - "AoR5LncBpmpv6WwxCqiEM96Z9Lwiim2bSAyZAHt9MudA": "AOR", - "39zSVsSHFqNhARbVh6n8ZF78nCmhV3gSg8D39xhBNe73": "AP", - "4aQ7cA7vnfCQ8opcNBRarRYcm6Mnwioee74RXpMHbonk": "AMERICA", - "9S4gfoHfA9Tt7tJka4dqKDRDS42uQDMAQMLDraCb4mfE": "AMERICA", - "F9NydCB1rDB4orWsEwenXmT71ZjmuRDsqLcYL2Fgpump": "APC", - "2oQNkePakuPbHzrVVkQ875WHeewLHCd2cAwfwiLQbonk": "AOL", - "Hxai4E6xQdtQJXZJTfs67mdFGfF22y78ut9heLHpqUSA": "US", - "SS6AEWhzRrxhL2cXzKKjhFt3rCzmHHGKmFyugDTondo": "AMGNON", - "AY1Ww6MxwC3cCiyrandHqLrp4FXvzgcwQTZpJ2FEpump": "AMIKO", - "H7ryQGvGeTFT4L6zVjwmombQPZHJ6nBbr1PS8tXytime": "AMOURANTH", - "4CmYCPDzU22ck8hT6AiPVk2iTHg9SeYtPCXZNuonpump": "AMURICAH", - "LmTMwmZLNZszn3qpjmnbhfP12U4qWDivaEBwSBSondo": "ADION", - "7iT1GRYYhEop2nV1dyCwK2MGyLmPHq47WhPGSwiqcUg5": "ANALOS", - "6d2Wze1KMUxQ28sFLrH9DKfgBXpUJSJYZaRbufucvBLV": "LOS", - "93RC484oMK5T9H89rzT5qiAXKHGP9jscXfFfrihNbe57": "ZERO", - "GYPxyPtu6g1NmnEPPkTcCdSZz7ZMs6A1S3wTjnLdpump": "ANARCHY", - "EZGb7aaSbCHbE6DcfynezKRc9Bkzxs8stP97H6WEpump": "ANCHAT", - "DVzrCErBzydh92bBzSJX1dKwVvb4omwhrvNz8CwRyxxV": "VONSPEED", - "FdwpkZZs78rUJPRMavGqfFDujYmCbxQFLWqupY5Hbonk": "ANDREA", - "7ekB2w3Rsxmg2YSL6axUHLmHEveJUbxBHRxmDSfX6ray": "WTR", - "PresTj4Yc2bAR197Er7wz4UUKSfqt6FryBEdAriBoQB": "ANDURIL", - "93n51i8anR3ziS6kxqXEACjrvJoXpAxt3qbbo3eaRFRz": "ANDY", - "69P4cjVxSbrGfks5bcAC5yJGwRBvyeZ9exDmhoVCitVT": "ANDY70B", - "667w6y7eH5tQucYQXfJ2KmiuGBE8HfYnqqbjLNSw7yww": "ANDY", - "GRkAQsphKwc5PPMmi2bLT2aG9opmnHqJPN7spmjLpump": "ANIME", - "B5cteji7DaRFWfY16BZeGhWMTaeX7RX1CokhbhCzpump": "ANGRYGUY", - "9tqjeRS1swj36Ee5C1iGiwAxjQJNGAVCzaTLwFY8bonk": "ANI", - "5nvNUh3ZU2Q3AJmD5YP8ykcUSv2P8C4Z8MKt5vmTpump": "GUNS", - "CQkgAQg5qCor8UMee5s8SzGkpjoYxCxrNzUuhwAnbonk": "ANI", - "45DzrxKPS4Rgerk7u6gBCB9iikF4gYVUtYE6GXbDdZkj": "ANIME", - "AnXE9mZYWReqBw4v5HrY2S2utt42uEtcBGmuCXASvRAi": "ANITA", - "4vqYQTjmKjxrWGtbL2tVkbAU1EVAz9JwcYtd2VE3PbVU": "WYNN", - "5su44fkvYNM1WnMCgteXu7HFUywQBtKZqjwgmCk9pump": "PENCIL", - "HqVZaYJnEcmKQKRf4K5N8eEuBjkTgpRzVfF7AYBFpump": "ANON", - "D25bi7oHQjqkVrzbfuM6k2gzVNHTSpBLhtakDCzCCDUB": "ANONCOIN", - "8VJ51bdE3xorQ1zB7FEa8CsHdM4kw77xCFiCgbnL2qbT": "ANON", - "CrMwppXm5kQEbXPGNH45ASkQyRbd7uonD8heVQsEbgSA": "ACC", - "FhvBDEr46meW6NHWHNeShDzvbXWabNzyT6uGinEgBAGS": "ANON", - "94WxCtGPFYdyWQ29VmqYbX6y7xbpUteHtX3Mak6Jpump": "ANV", - "6n7Janary9fqzxKaJVrhL9TG2F61VbAtwUMu1YZscaQS": "HOBBES", - "HAs8hvTB8ZH6dBG26KQGik4fxitNYi41jnYd49bvtime": "ANSEM", - "BfHkvKMEYjwPXnL36uiM8RnAoMFy8aqNyTJXYU3ZnZtz": "ANSOM", - "ANTT2Ve8c3JC6fAksFxUGtByzEHmJs49EupP7htA5W7j": "ANTT", - "7VnT8zHzorYS92snKC4CZU2veigEVnVVBSxTw7G1pump": "ANTS", - "Pren1FvFX6J3E4kXhJuCiAD5aDmGEb7qJRncwA8Lkhw": "ANTHROPIC", - "DGNm6z68D9iagDgjVNiSfadmyLT7vyNejDJ9jyy4BABm": "925", - "9rTrsxzcDRMKpBHsbmdMkfPsU6K8FHepcyp43Dr6pump": "ANTS", - "ts3foLrNUMvwdVeit1oNeLWjYk7e4qsn8PqSsqRpump": "ANTIRUG", - "HB8KrN7Bb3iLWUPsozp67kS4gxtbA4W5QJX4wKPvpump": "ANTI", - "BYdxrskh1HfVfvAd8a9aAjoLAU6Bo97AgN56gDwqpump": "ANTS", - "G8s88jzPhiKdfCmwTKaShErB2VrbrsvDsDBzsXqjpump": "ANTS", - "ACeWC77UeW2DBZMe7YBsuXoxLvk4dHMnPzneApau1Au6": "AI", - "5fj8KkMfofVY3LmuLiYdxS7VprhKoLCwek57UzYYdoge": "AOGE", - "2b6evX4o265k7MAU5QtTsw3G2g5uz2SUbAVkFr3gbvB2": "APARTMENT", - "Ghp84ZTWgJhATi9UwnTSqzedihUsvup6Ejwiz4TaEA25": "APED", - "DF5yCVTfhVwvS1VRfHETNzEeh1n6DjAqEBs3kj9frdAr": "APE", - "984GBL7PhceChtN64NWLdBb49rSQXX7ozpdkEbR1pump": "APES", - "ChNrhBZwGtn1ZRsrdzcuBqiKTYfVViCPZqJdsgBHpump": "APES", - "KdF3kjqSHbWXFstBLA2dccDxuNBi4o3yZqtMhB7k5dZ": "APEWIFHAT", - "6rE8kJHDuskmwj1MmehvwL2i4QXdLmPTYnrxJm6Cpump": "APEX", - "51tMb3zBKDiQhNwGqpgwbavaGH54mk8fXFzxTc1xnasg": "APEX", - "6k1rwsNMBwbRJrWck3sEVCjn4MvF9ty8A6iSGfxjxREV": "APOLLO", - "FubtUcvhSCr3VPXEcxouoQjKQ7NWTCzXyECe76B7L3f8": "ACRED", - "14VXAhoa1R74vi1ZuiQyGLJrnDMfoFBPJSCpGVz3ondo": "APOON", - "acatzTjUeHDT3SoufN6NMxGUmBFtoqFHnFwusdw8kYX": "$ACAT", - "123mYEnRLM2LLYsJW3K6oyYh8uP1fngj732iG638ondo": "AAPLON", - "XsbEhLAtcf6HdfpFZ5xEMdqW8nfAvcsP5bdudRLJzJp": "AAPLX", - "B6WqvLGXdGqpw7qgxeb5EGiRZEYo2apWpQybjYuondo": "APLDON", - "7eRX747PSbVtGVx3qD5UFdkNM2BfTy86ikUiCMhondo": "AMATON", - "14Z8rQQe2Aza33YgEUmj3g3QGNz8DXLiFPuCnsD1ondo": "APPON", - "XsPdAVBi8Zc1xvv53k4JcMrQaEDTgkGqKYeh7AYgPHV": "APPX", - "AhPo2fs5cfUyDhG7cvR67wwKg9UeFsF7mng368EB4oCn": "APU", - "ECutGg12PNhqhkvnH1s1FcuXgCDzKDNhSf5aLtANioR7": "APU", - "26s3UGB9hund1qspApy1zYgCritxAooGg7o63BMn89Yq": "APU", - "5JnZ667P3VcjDinkJFysWh2K2KtViy63FZ3oL5YghEhW": "APYS", - "5EkcKr582FkAPVnRmaDdnkekTtnNRzNNSxmQxqrFpump": "AQC", - "C49Ut3om3QFTDrMZ5Cr8VcTKPpHDcQ2Fv8mmuJHHigDt": "AQUARIUS", - "5mXAnC2LWxvNF6und1Mokrv4WnPDP9uxzRsyWATYpump": "AQUA", - "BFpchrNVhyTRzMNAg9QkiZfRN2vqRBwcYoTX8qgkbDvm": "ARAB", - "EDcdVtzW4HG7Xtg5yH3RbzMEyfudg63RmZgdXpQFUk3s": "ARB", - "KcCVQxG9LhFYP5o9DWFKTFgFShPPQkDEemVbiFyondo": "ACHRON", - "4VdSpMVR2ehiL3SNQyehEY3WwX2YhhpWa9HHMfBbbMeW": "ARCHIVE", - "AdxNrCntq6YkYC2qVffF2Fw7WC1UatKhV8J6G4ekgNx3": "SON", - "9XRpjZjhJPeWtUymiEWn3FW7uAnMeQca14ucTWWWyP2g": "ARG", - "EbD3Qptmezx1mEwMfa88q5L5CrA4QrBwmvvZUhEapump": "ARGUS", - "GhBPHgnNF99EThYxt4bxUfPX1hCPyAS72RCBgLjLpump": "ARIA", - "GhFiFrExPY3proVF96oth1gESWA5QPQzdtb8cy8b1YZv": "ARIES", - "4ZiYgt9nyPXvSfBWADGYHiWnTdnogrFgLTALdFWVpump": "ARIE", - "Cq6QtvHpXbJWtFaiMhUDtHy8YVZ95gcD1oZ1cohondo": "ANETON", - "HZXyLwwoxWHnnxfwFnQSTTBs3ys1xJsTQG8CzhUnzray": "ARISTO", - "7ztJWBzawnLB1UeEC1zRiyuxqhd2r3oxYYD2ouKVnBLV": "ARITECT", - "3VTXWW93L1WRfQ7QDPjvfBQgzFRVqDgHPnc8vwQmEpUi": "99CENTS", - "Cph1zxsCUz18v6aa2hiUYwfVkSV7BSFHmCaJLqQspump": "ARK", - "15SsCZqCsM9fZGhTmP4rdJTPT9WGZKazDSsgeQ8ondo": "ARMON", - "ARMYZt71GXq4vw4mtDs5LnEp4ZgwWKEE2CdMU3WNnFEC": "ARMY", - "3Gdx9LmKZJyciSz3mbge3YKLqskiMG71m1iDMPAwpump": "ARMY", - "Cwe5k2iEy8dt7vCGY93GBrqQS4FWyac6pQjvjLcPD6qp": "AROK", - "5RDjKNvXqs9Y5gkrThtCfpicZYYHs9MK7HhdXdUeaBLV": "ARTI", - "EJRs2ephB8kZX1w196DBt7q3uvLAVqs6LEVgXEwhpump": "AGI", - "7JH1tgtDjze6miPsFV3THUFh1r25JYUHdZsM6q9xGqZ5": "AGI", - "7tAnusL3srXjzEriVL86a23KgwDZKXJTZ2ezEUubonk": "AI", - "pGmqZA8iruRh7dtRH4x22cU4H5wPPWsJUA9h9vapump": "CLAMS", - "61h3Lyt4BtD4g5YqztbHPUZE742sPU1kG3bfMxLpwWaV": "ART", - "ATRLuHph8dxnPny4WSNW7fxkhbeivBrtWbY6BfB4xpLj": "ATR", - "7rJp1KhptfCXd6K46bQzaGRYYUboyPE1u6fzEYBKffZB": "ARXIV", - "9DMD8SzXwGLsUDbmJmMza8cEZLLsajZQvZ9tWWKepump": "ASCIIVISION", - "45ZAM7JK8ZGHuBQiJ8kvhdiVdiQGsTQGgt3gRAEQpump": "ASHA", - "4Uf883orBx89VQZiV4EoRpNqUWYN5ZuEvC4PCpQ9moon": "SPLAT", - "EG6WeiR2DymUVVjNHEU4MEJXmWnzXJhk3JerRy4gpump": "SNAIL", - "1eLZPRsn8bAKmoxsqDMH9Q2m2k7GMNp6RLSQGm8ondo": "ASMLON", - "XshuHQ6o6SVpUNawvnnTMxsZ4tacZsNgVCLorv7TkFq": "ASMLX", - "12XbSPVc5hmWjKUzf5ExTysM2pEL3tM953YkMXmLWkGd": "ASSAI", - "G3EDZoS49NRVKP8X1HggHZJueJeR8d2izUHeXdV3pump": "ASSCOIN", - "7Tx8qTXSakpfaSFjdztPGQ9n2uyT1eUkYz7gYxxopump": "ASSDAQ", - "ASRRjA1R4RHVk5H9QKKm1jaQqMkxvv6nh5EypPrvwmxQ": "ASRR", - "FRUnVTbSteDWYmn8URfsJkHYgfu9MDxjz7vwYj4fpump": "ASS&P 500", - "FLxwDc58Fnb9h1Yq6Vc7YhXjFPwzZadhLPJpUHGMjups": "ASSTEROID", - "HzN6MGRCi4QMpAeqvKnqsDDodAq2LWNdxSkPHnCpump": "HERIA", - "Xs3ZFkPYT2BN7qBMqf1j1bfTeTm1rFzEFSsQ1z3wAKU": "AZNX", - "8GQa5N9HJT5tW6vg3umYG5us1xHmiU948LfDihg2f8Vf": "APX", - "B6ry9goGNvVbhq7gWHzs3p6emJ1gLaMhu4By9TTondo": "ASTSON", - "XsR4LAtaBgTKTRUhiijY1ba13nx4bepeEcag2Pr4dZ1": "ASTSX", - "DNMTk67urDBxEEVRx9HjVzCVu8en4Kgg5HfZy3E6pump": "ASYM", - "9ZDghDhVrJih39y98dADEPKL9o3WLwXcLoks4Gvqpump": "ATNA", - "WKMZummev5UcXz5nNKQZvTD6QjNSM2X58uwmDReondo": "TON", - "ATSCZA9yeRDoLQQMTiiKDF2gW1gkFtzNaRr4tQXYVz16": "ATSCOIN", - "Kkkhb2uaCYwXoTrWC7CMPLa7T5UsbVVeeiQXrTSuZUT": "ATTN", - "FVGdJQVzAdKr8ZH7ahyhp4cvDszX3N8QYGSdzueopump": "ATTN", - "XsqML71RLbyUM3CmY4EYN88zE6BJkpvJACnUKBdGD3x": "TX", - "AT13ipG8K4HDyEJm3H1ZVQV2Bw8sihewoLQReSMsYo3o": "AU79", - "9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM": "AUDIO", - "5EoNPSEMcFMuzz3Fr7ho3TiweifUumLaBXMQpVZRpump": "AURACLE", - "4rwPNRSFgcS7EGphFdX7VwXuhjZGxph7gYyb7Zp2pump": "AURAFARM", - "5eTuF52LmxisGfPPhZeiUkBPMdTkF3WfF1w5P5FUbonk": "EAGLE", - "DtR4D9FtVoTX2569gaL837ZgrB6wNjj6tkmnX9Rdk9B2": "AURA", - "CMkj12qHC9RjAUs1MED38Bt7gfyP3TbEpa1mcBno3RUY": "AURA", - "9tF4vuYRQY3d5GPnE9pjUevukgo6vHiepe3E1w8Jpump": "AURORA", - "bozdUuCb2kdipxES9PzsYHmDVrf7FBTSW3p1CFBpump": "AURORA", - "3YmNY3Giya7AKNNQbqo35HPuqTrrcgT9KADQBM2hDWNe": "AURA", - "AURYydfxJib1ZkTir1Jn1J9ECYUtjb6rKQVmtYaixWPP": "AURY", - "9h5y3WCqfeNt2fiAP7iM2ZRp9apFb58ipvTzcWi9pump": "AUSBAGWORK", - "BkVeSP2GsXV3AYoRJBSZTpFE8sXmcuGnRQcFgoWspump": "AUTISM", - "8jiVXftnn2ZG6bugK7HAH5j2G3D6TpsG521gqsWwpump": "AUTISM", - "6QbkqyarW5ET7TvNsENGmV7e4fg7SpACdYb8ba5Ppump": "AUTISTA", - "9QRknyCszLEmzgfEHuW4v8cygf4Hu5Dwc1LjgSaRpump": "AIT", - "8QhSMvYfXome11VgxFMD75hNbGQXW5QTnjA8khENkY2c": "ATM", - "Ez3nzG9ofodYCvEmw73XhQ87LWNYVRM2s7diB5tBZPyM": "OLAS", - "GNp2Vzrf5VfjA1vRF7MyzpcC3YfF8LEiJLY7rNsRpump": "ATNM", - "6d5zHW5B8RkGKd51Lpb9RqFQSqDudr9GJgZ1SgQZpump": "AVB", - "DKu9kykSfbN5LBfFXtNNDPaX35o4Fv6vJ9FKk7pZpump": "AVA", - "H1vAwYTZkL2Bq8tpS5DhC4pPJjhMLviH2ScGjuePpump": "AVA", - "KgV1GvrHQmRBY8sHQQeUKwTm2r2h8t4C8qt12Cw1HVE": "AVAX", - "63rvcwia2reibpdJMCf71bPLqBLvPRu9eM2xmRvNory": "AUI", - "HdYskPuX5cBApzGqFtoyKdzLxfsLCQxucQfmX2FiquWo": "AVIAH", - "BANKJmvhT8tiJRsBSS1n2HryMBPvT5Ze4HU95DUAmeta": "AVICI", - "GdZ9rwHyKcriLdbSzhtEFLe5MLs7Vk6AY1aE5ei7nsmP": "AVO", - "EE5L8cMU4itTsCSuor7NLK6RZx6JhsBe8GGV3oaAHm3P": "AVDO", - "9R6qezCXoY5fX8nC7SA4bZ1zHrwG9WnnKVGt1SRoMGF": "AFK", - "7f94zk1EgfoeG57Vj5FtDDjMmPNHM4DYs7KRiyd2T4bA": "ALMC", - "Dr7r38EvuFzezm9FhHgVw6eaPjGNRaj9JMAknAprpump": "WHITE", - "ovutZfkUMXRBEH4hAXJE42j2GK3Az9ZjTdbG5taWoKe": "AWOKE", - "9tLUnDz6G2dUGVhiLEEEpfM8e1YBiXnWrdT4xVeopump": "AXI", - "HHf2VfXSPUVqQth6tXypfdUX2vawCiVKfkbAX4CJpump": "MAXIS", - "4TaBYbgQyErDQx67rJRnbABd2tqHCzBvMN1a2M4vY8fc": "AXYC", - "5JP1Loni4DtDgvHupQAzcRXQtsWnWWVibdsD4igVpump": "AYYLMAO", - "CCEDUV4wfDUog2Lb4o1vA9Wpzz4WXn7fqf5zsW46XuS9": "AC", - "387y7v7vPvCguwmsuN56QuxankZdES97ExqSp9CNpump": "B", - "cBnVXDyZgaaLZM18wAmqsUKnRUFAEJWbq6VuUoaondo": "BTGON", - "Bpn193geWPDvWW2po4cr5hpudLha8LeZYngmWHh2bonk": "B33", - "6dEJEuFvaD8jMyotbvbMx7ixpP2frXnZCSgDexwqkE6B": "BABA", - "FhrFGKvx32aSJW4tdygWnjEs8wKnD4FzxyjezBLg6sfE": "BABYAG", - "HkrchFiWgRuPTdmNvqrPRLANDdeuuCTQERaB2dJSpump": "BABEL", - "5hmf8Jt9puwoqiFQTb3vr22732ZTKYRLRw9Vo7tN3rcz": "BABY", - "FjtQhyaGmYioRKA3enZkq3wVXLT121DwQ24W9d5Vmhw2": "BABY", - "8467ssuj6Gkw15ABv6BvxJAKnGJALXJu6dxDxt4upump": "BABYBTC", - "Dx1Lq5FjangW5ifRMEogAiakm24LyB5AoHmQifepvNjV": "BABYBONK", - "7dUKUopcNWW6CcU4eRxCHh1uiMh32zDrmGf6ufqhxann": "BABYDOGE", - "9ceEjz32cv8jBcqsppgjrryiE2tor7PCm7j9mYk8gzTk": "BABYWIF", - "AnUmd399EDCEmoXjSpUAVWE4TcyT1xFMJcPgAZ2VgChn": "BABY DOWGE", - "3Bbj7eZTuMd2FrfeZ2degzckxhgB5b63crGoHLtrpump": "BABYFWOG", - "F4aLcMxQy6CPcXAuER3J5QgB89n4fqBMs2bcrqQBpump": "BABYGOAT", - "8uSFaAvg2BHZ3QYAnkshoi6AxErPJvJG53NsjKjDbonk": "BABY GROK", - "HpLfPx1NkMghAEVq6d2nv84LiZt4nKvGJ2aRiUFno1DX": "BABYDENG", - "Ey2zpSAJ5gVLfYDD5WjccbksJD3E9jPFMPaJ8wxvpump": "PEPE", - "z9ysNwXCYrh4xXRyQG9ujWqxNrdcChoicoJA9ebd7iF": "BABYPNUT", - "83xejQ2QoHWD47REppszZPbvkzHUyem5SSkjyXBFieoC": "BABYPOES", - "Ej9jJDCD85mZysgorrY582Vpv7HLgDyi7Fa2jRCjgscZ": "BABYPURPE", - "Uuc6hiKT9Y6ASoqs2phonGGw2LAtecfJu9yEohppzWH": "BABY", - "8nKP8Vc72pRZB6bhCy8D1UYf6ZjwYT859i6awyinpump": "BABYSHARK", - "5Nu2hLo3ffU4fs2yVtsCvAT7ymysuYs5NSFRpvLMVSBi": "BABYSHIRO", - "8MdkXe5G77xaMheVQxLqAYV8e2m2Dfc5ZbuXup2epump": "SINCLAIR", - "bsAwuVJMPAazbQSVpPQND69mvwftQutZhxpWqsf2BUE": "BABYSOL", - "54meDtup2K7pqyhUK24AymoLzMPtrkhNTbEzQ6gSpump": "BABYSOL", - "13RbxjgWhXhgSN4krVwadunLSAJjph7b9QFU4GWEpump": "BABYSOL", - "HysNUGUyrapf4A9AtkDz2JaAHCQKmv3rsGV41mdVpump": "BTRASH", - "M3NC8TeMfosXhHLZC26BP8Et2h6ozcCXYF18nx4pump": "TROLL", - "7xAinob3Fmi6Sf1zA1kUDkuSuBh4KEVtuKqL6WM1DWKr": "BABYU", - "7pmuGLLYdJ2mc7chZwEJAaxuWALAYqaVqbUwzzyHcA7D": "BWEN", - "8sbaCMtqUKHwmPkBfe1m4u738u4MjvTCDbRXLDjppump": "BEET", - "BPxxfRCXkUVhig4HS1Lh7kZqV6SPJhzfEk4x6fVBjPCy": "BP", - "BPSoLzmLQn47EP5aa7jmFngRL8KC3TWAeAwXwZD8ip3P": "BPSOL", - "E7MYzWD8GtLYNsgRyECSts2p1s2mu5JLb6wR9CiCBAGS": "BB", - "EW7cWbNmTgL7PLQNiJ6tBVC62SJzJXa2pFYJjDPPpump": "SOBAT", - "57Tfcj7Ak3qpBKQQ2k5LTrsJLg3csDdY7Q1rC6y5PumP": "BADDIE", - "8kp7gVCTWVbNP257A7MHgsYheshqYGnqWbApS6f3pump": "BADGER", - "777Z92HqqGEav312yx8CfakajosBRFFUMbemyLaqbonk": "BADRUDI", - "GvkxeDmoghdjdrmMtc7EZQVobTgV7JiBLEkmPdVyBAGS": "BAGLESS", - "HMaJxXp3Ja81jSwigF6zGaMt79hNZuczrw6i7LjTbonk": "BAG", - "7Pnqg1S6MYrL6AP1ZXcToTHfdBbTB77ze6Y33qBBpump": "BAGWORK", - "54CoRF2FYMZNJg9tS36xq5BUcLZ7rju1r59jGc2ondo": "BIDUON", - "4tViJmZ7LKREaqxrh3tXeCB5TfK7o67W3WhykHrr49ZC": "BAINANCE", - "CQbXk942c6GPcRwtZ2WMFP5JcQ9NqbXtb8jUewBi7GoT": "BAKED", - "Hz4CJCpZky4YD75Zs2R2nBjP5wY6tYbraaT2hVZCpump": "BAKKT", - "FqnqT1GKi8S4Gyk5wnSKvJjXW48HqGtKJt9WS4o2pump": "BAKSO", - "BH4TZqN9TXnkjiLEQZ9xuXo85YaGoonM4PHpcjHEoTAx": "BALN", - "31ovHdpQJTMrmxwiSRWpZQ6oY3AP9wu8RWZh8adepump": "BALLIN", - "6BFPDdf7VdkFdzePjWzVENzgigzs1DJmZJhKtjiTpump": "BALLSACK", - "4yCuUMPFvaqxK71CK6SZc3wmtC2PDpDN9mcBzUkepump": "BOF", - "xN9Qd63mUYg7npanmdksmcqp3NQjTcGFQPTyq2F1TQC": "BAMBIT", - "5sM9xxcBTM9rWza6nEgq2cShA87JjTBx1Cu82LjgmaEg": "BMBO", - "CWY3XDTbN91sSmg4XR3ZgxAVgrhiFv4Ue8gUwwJLbonk": "BAM", - "31GUGbRSct8MsJJnditGy98xMxowq58sFsZ9CGQRMLT8": "BCAT", - "Co4692bPPQXAqqxSLAeTTvF1bnhY26FUWt2YuWQPBgfU": "BANANA", - "FuQASH8ps9NPeDu4h3rVtMBygKYoSiSTZ4uSiA5tpump": "BANANA", - "4ytpZgVoNB66bFs6NRCUaAVsLdtYk2fHq4U92Jnjpump": "BTW", - "BMocPvavt5CLXvJpQohTRbg17WhPMfkFxibHEQpSpump": "$BANE", - "8SVVCGzYwnAkDwwvc5fSHZdCenUyhPccnGirWecVpump": "BANGCHAIN", - "HmRpmbeGosahTzChmTmKzFHR5SmgSgXFAXKumajgpump": "BANJO", - "k5AMg1yUxaYTNXpVDpkrVn8VSgBe9K4t1pQYVCzbonk": "BANK", - "Wk8gC6iTNp8dqd4ghkJ3h1giiUnyhykwHh7tYWjondo": "BACON", - "XswsQk4duEQmCbGzfqUUWYmi7pV7xpJ9eEmLHXCaEQP": "BACX", - "EecSMXvvhszENGs7SAWMejLgBHBAhCvkMDWY88JR7hog": "BANNED", - "BXFD1Dd1AT87MADC6SYZ95QQQjrTKEnDRwvVhAiLpump": "BANKSY", - "BANXbTpN8U2cU41FjPxe2Ti37PiT5cCxLUKDQZuJeMMR": "BANX", - "4bXCaDUciWA5Qj1zmcZ9ryJsoqv4rahKD4r8zYYsbonk": "BAOBAO", - "7Da1f2wibgzZZheXnVvqYgbWpccnGPM2CJPUkdcebonk": "BAOBAO", - "UHaVCzi4ZsGrzHhAFbokkXfaugfYbkdVME8XYaR7eKu": "BAOS", - "6Gci64E133ZSQ8VCMrfWQb9ekVNAwHzRobqt4vkjbonk": "BAP", - "8Ga7ExC7toM9v1PqCB8jjKRTKqiYqQGLCua2VnXdbonk": "BARKCOIN", - "3B1ijcocM5EDga6XxQ7JLW7weocQPWWjuhBYG8Vepump": "BP", - "Em4rcuhX6STfB7mxb66dUXDmZPYCjDiQFthvzSzpump": "BASED", - "275K6a8XzouqPaX3Ue8QaafSL5kw8dL1E1g8jwHzcmpL": "CHAD", - "8Y5MwnUM19uqhnsrFnKijrmn33CmHBTUoedXtTGDpump": "BASEDD", - "EPeUFDgHRxs9xxEPVaL6kfGQvCon7jmAWKVUHuux1Tpz": "BAT", - "6gnCPhXtLnUD76HjQuSYPENLSZdG8RvDB1pTLM5aLSJA": "BSKT", - "EtBc6gkCvsB9c6f5wSbwG8wPjRqXMB5euptK6bqG1R4X": "BTC", - "C3JX9TWLqHKmcoTDTppaJebX2U7DcUQDEHVSmJFz6K6S": "BAZINGA", - "Bv2AYefUcEe8StuRAAsvGLyeB8fkqVeEUVvYAiPApump": "BQC", - "coqRkaaKeUygDPhuS3mrmrj6DiHjeQJc2rFbT2YfxWn": "BCOQ", - "BCNT4t3rv5Hva8RnUtJUJLnxzeFAabcYp8CghC1SmWin": "BC", - "CqMybyWjNGn2FGt1JY5HDUGMiCpQjAsyj44csfMUpump": "BDC DANA", - "BMBtwz6LFDJVJd2aZvL5F64fdvWP3RPn4NP5q9Xe15UD": "BMB", - "FdFUGJSzJXDCZemQbkBwYs3tZEvixyEc8cZfRqJrpump": "BEARCO", - "PvhC1i1uu1xmB8bsaoTj3mLf1ihYdQo7MtVQebppump": "BIB", - "B7sn8nJZtGSQzR7dUwSuLALGWMG16PmASRTqC2Pspump": "BEAR", - "EGedTSu2zdFNT1k7phURksCpcwHNyxepLzSMYGApump": "BEB1M", - "brBg8x9yT4WnQd8Z43aX7wfrGMrimLfT2WCWRibHo3d": "BRBTC", - "B2BsVuodH2eBGRnFckRYYW6yjur7Tn8cCxaSE2Xrnj5V": "BEE", - "BeemGmKYvz52af2myrx7xE82rE8ZvW9MgpvX8ikJQZNe": "BEEM", - "9sbrLLnk4vxJajnZWXP9h5qk1NDFw7dz2eHjgemcpump": "BEENZ", - "FHT57nzPTw6FFk3XK2gjFsCvxqYWbh6qM8dEiGK7pump": "BCI", - "GCKTFLxkMeUTUbuQM1k6f1cy4o4tC2V13ye2ebL3p52r": "BEER2", - "AujTJJ7aMS8LDo3bFzoyXDwT3jBALUbu4VZhzZdTZLmG": "BEER", - "2Tr3i2qjSPUb7e7BSFWTq7gsRzJEnYBKc7wRAYu3pump": "$BEFFAI", - "FdzVFSP9KX58GzpJywdqhAmADr3XTEypawHyenShpump": "BEING_AI", - "CQhbNnCGKfDaKXt8uE61i5DrBYJV7NPsCDD9vQgypump": "BJBN", - "CP4w2B3og2TaFUpye1kr8pdeJwwahtESKppZnffN9n9d": "BDX", - "GT9SetU8UWKJeCQsmMcNHH4pGPVSKrxgQsgu2wtYmZHW": "LEAF", - "HoQYRCnUeyZZyFiPtYDw48kHyGwjsaUXJxDVtxcK4yPg": "BELIEVE", - "5tRLPc3JsbMh8QU7EebaPvTpExinXNA7B7R9MQcijBLV": "PNTH", - "2GPJhV9jNrj7TaLYMRgWkcy6sTKLcwntv7nZ7qDyMRGM": "BIS", - "EmhVWGUa2Q9PN4QjzFjvuVyzFKg4s3ERwjKR9dQDpump": "DTF", - "H1pRLTGu9Q3Kv6ziV9DCvTxNQA2vAVocYuGb41wZ1FBk": "BPAD", - "4PYijC1Xas63cxdoYnUg7SQgcg23UgNNcPmvbj6KNUSz": "BSCREENER", - "92kxUXDsBQLy5ptGxtmH9CDYd5E6ZQynzf2eFPiUpump": "BLOB", - "5bp5PwTyu4i1hGyQsRwRYqiR2CmxyHt2cPJGEbXEbonk": "BUMPER", - "AuPMJ2VqiJd6gd2e65ALYhpSctzCYancBYLG5usfBAGS": "BEL", - "5xL15htnLsMN44DyUQyztEYSFBn6pgF41cngBkKMpump": "BEL", - "E63CDwLy9Dwr3EptAzopV9RuWoQnn5ZVYEjLWnJX8dCw": "BELUGA", - "HFtLLofzSqJUP5Hk96jyrBjyZq6PcUjfSA8AbKoopump": "BENHAM", - "BLVxek8YMXUQhcKmMvrFTrzh5FXg8ec88Crp6otEaCMf": "BELIEVE", - "AHW5N8iqZobTcBepkSJzZ61XtAuSzBDcpxtrLG6KUKPk": "BENDOG", - "FTdkGcfFUDcXbqRQL3YeLYJa3e3Pnaz6kJH1sBsapump": "BEPE", - "5kkkNahbYVYZUQthCZCU6ax4zkcN622jAMTnAvJApump": "BER", - "G8XfMyCLW82Hq2duaf3Kw9hQXf9ntLsPvSx8qZhZpump": "BER", - "Xs6B6zawENwAbWVi7w92rjazLuAr5Az59qgWKcNb45x": "BRK.BX", - "HgBRWfYxEfvPhtqkaeymCQtHCrKE46qQ43pKe8HCpump": "BERT", - "FQMoiNiQGLngizBVdhxqD1o2k6rvcX9ComxjKucgBByH": "BESA", - "8f1zccZPpbjz17Ay9wZKT5Mx2oPtUyxAVz5p5yzEbonk": "BEST", - "93EpfRSroBwD5DmDBXkaCdLZy8nnShtrVCLMCXjxpump": "CULT", - "CKrVUhFFKxjBshAWHE3ZkiPiBTkN3CfrMfbdWBH1pump": "BETA", - "A6jBzMFsrxGEi2s8SDejWHojGu2j2RF7LvwTD2NTpump": "BETA", - "A1r1SooLp5GHZoAWmY3BErwByYoCrV2StAiCEa3epump": "BRUH", - "7uJrMsDN2Wxdc3VAq1iK9N5AHaTA7wUpbm1wqRonpump": "$BETH", - "5XgpGK83mxVdcGuZxv7My99ZmuSzdJo4noLaaD4bpump": "BETLY", - "5CLSw4viyMLfnRHHxZpDF8CuZt5e1ceq1AJqd4GjAJNX": "BET", - "BMHkAMoj2B8VUPPfEkops8XqPgN4oJkjR2oFRpRG2oqL": "BETURA", - "7gsdvvoREQbKesWJCDCb8reeM65yQuQDwT2jcH1JUG9V": "BEZO", - "HUhmmhEiT8H969TS775PiF9w3sDyHgBYkMjGiyMQpump": "BEZOGE", - "57Wdx3uikKcAt2w8F9GMwgGeEvkaU6pXacYScW1hpump": "BHAD", - "3W52uCb8NW8ruMF9mmJX3oKiYAjdPai4633srsZFQCS6": "BIAO", - "2ru87k7yAZnDRsnqVpgJYETFgqVApuBcwB2xDb19pump": "BIAO", - "7UiTAJhzDcqc5CvVAUWS6sUyeXcQWUXuh7zsbUrNzb8K": "BIBI", - "4YuD8JVXFeyeowJacXMLY3QKhd6NwaJhto1Xqrnwpump": "比比拉布", - "GkJxELgJXpQRm7dfc2yS18vBDRxP5SjVJgbrmTGgpump": "BICHO", - "FT6ZnLbmaQbUmxbpe69qwRgPi9tU8QGY8S7gqt4Wbonk": "BIG", - "YXE7mph6XhsgnyezkMEcTuohSuWhbLWfwx2Hh6mondo": "BBAION", - "BhwwEnvzsEdncGWdDZv2airsw1HZVbRBBGfYhgZfpump": "EAGLE", - "ATBR4i19gcQ31Rfr7ymA2XvkCQEAkNFGBtVKTmdqpump": "MACHI", - "FAKDHD3GuHMvHmgMPgFKNknZJDEnGT6AQFipaQ4ppump": "$CHEUNGUS", - "H5gczCNbrtso6BqGKihF97RaWaxpUEZnFuFUKK4YX3s2": "BDE", - "CeK6k4BMmiqkxkzYAbQuUp3hrVFSHxuQ1LJXmNybc3vz": "BINK", - "8U5SJfExR7QMhPWWKLFV5F9L8mX3vioEP29GxocBpump": "BIGFACTS", - "4mswMCYRz4ifCPh53b48ecXJvwurCJRqMwKSHuP4pump": "BIGFOOT", - "HbACvu9eKjCJE7h4oGFSnX6fvbDqwugRGYeuJebRVBkR": "BIGJIM", - "CX9YDTED9TWgVXVoFy8JL9gSSAEWcTv4mzjLSv17LQsj": "DRUGS", - "FtrH7NCrPDkhDmKCCdTnsDBFxiibc1X5aJPHqKQnpump": "IMERA", - "14kLsQVmc64qZexYuR4XGop9y8BeMkd77pJUm1Rhondo": "BILION", - "A94X2fRy3wydNShU4dRaDyap2UuoeWJGWyATtyp61WZf": "TRYB", - "wCtiCRJz69a5Mqkk2nHmvQwBGQCrUvM8fELoFGqpump": "BILLBOARD", - "BDCs2xEqzXyRpp9P6uPDnAvERpLKBfzHPEzbe3BfCxDY": "BDC", - "DdWVqFmdQpyjdU3AeUSEhrsnJQBPH7iK9xdCFrBgpAy2": "BINU", - "5o817YNoR97F1h5suzBghwc8dP53oureRuFbgJiaCFuZ": "BILL", - "3B5wuUrMEi5yATD7on46hKfej3pfmd7t1RKgrsN3pump": "BILLY", - "9gP2kCy3wA1ctvYWQk75guqXuHfrEomqydHLtcTCqiLa": "BNB", - "BNso1VUJnh4zcfpZa6986Ea66P6TCp59hvtNJ8b1X85": "BNSOL", - "4ysWSPRkmqY6Y99SN86D2MBHXrvnR2358SaGb47Fpump": "01001000", - "BeFtLwLtS9Rva12KrHKRMY1H5WoeM1Y2ULMnNJKopump": "BINDER", - "ABc1BqTSZEwZoZr6kJUyUCMQTTxWSh18DyK5GM4Spump": "BINGUS", - "AQuuQ4xktyzGBFnbKHnYsXHxsKVQetAoiPeCEG97NUJw": "BINGUS", - "DMYNp65mub3i7LRpBdB66CgBAceLcQnv4gsWeCi6pump": "丙午", - "9hfajwF4pPRtLG1psEq5s7yBCSieKkUXGSUrvJXXcmcH": "FIREHORSE", - "7dxQE4YXrWqtwUVb8W8HjfPT3t4H4dWZfsDWRWKupump": "BIO/ACC", - "2xU5oEz6w8iHyLyarGiMdeVYTTyR3uJTgvZdzMYYBiok": "BIOK", - "9oxGicd5KXNcthYM6yD23X9zcznQVnHjrAEN38fFpump": "BIOLLM", - "bioJ9JTqW62MLz7UKHU69gtKhPpGi1BQhccj2kmSvUJ": "BIO", - "qcdogbnRfbhadxEjAwRuBoMJXPPt7z2Csm8jjtbB1Po": "BIPO", - "2EBjqPYGLUExdWwJJRLqtGPawzb2aMjE1wTpUYKhy2UQ": "BIRB", - "3XTp12PmKMHxB6YkejaGPUjMGBLKRGgzHWgJuVTsBCoP": "BIRDDOG", - "EUxS8Kqvbe8zDr8bG1RYtgjVNzEhMTG6B6H3WwrCHfwA": "$BIRDFLU", - "FTPnEQ3NfRRZ9tvmpDW6JFrvweBE5sanxnXSpJL1dvbB": "BIRD", - "4NTREQyLwcxYddh6TmmiwQt55zvGtngCLyYDBJQ8pump": "BITBONDS", - "BSkz8QHwk1DaT3Dcf1L5vWsCRzgrfrLR3qYmwSt1rtUM": "BITCAT", - "3XFiHA2gexzBjqtM5Z7FjJhP6f28D2m79UihBCfkpump": "BTCAT", - "4j9bDg7iWNah1Qa61rrqwWZMtEdqV3fV56SzyhfNpump": "BITCAT", - "78KiD7Y4rWuKQeyfnue9ayYNTzqxXvv2DK4bBj8Wbonk": "BTC2", - "DL9sLSN488yMbots3wsbzHZ3UpKSkM42kr1y13CPpump": "BOB", - "GTiMaW6FNPLrWNVLYk5KkLApxiHvCwbGr9KdueGypump": "ISAAC", - "6WNva7iLjTvxSfXPSmbjceW5Yc41LUH4SJNqKom5pump": "SASHA", - "F1Tq1a8NVJfgimf1dDyJivhFxah4tT82fMvFJn5Xpump": "LOBCOIN", - "G9ivB7K41a4G8m1k4QdxxN4L5eGKL7Mr12S26B85pump": "PIZZA", - "7dGEYMPsAVxJY3qQJaCHwLPkCCx9SSE52H4k1wF617uE": "BRRR", - "8jbjDAUAvXuh3tGjqCWUve71rWd5QEbgjYwsuq5npump": "PIXIE", - "5Xc1AycwC2ENTDWwGV41twqCyKkeoAfjnqLGMKKd7ytX": "BTM", - "JmQCbJEiPBDkvfjDLPBRxeuR4QXX8JeRiMQvUSppump": "MIM", - "8sv4W4uQ9qML87Kr2XFkYBDwsiFEJVZZ1ScsM71Hpump": "BWAH", - "GiSC3EA7C2kZwESb53cJybcmeRRExM4gqzV5UTz9pump": "BTCWIZ", - "F76Go1TqbZwnG17BhbQMovEJQHnmXuBBHAjGd3v1pump": "BITCORN", - "FSqH8gHUdt87mwJJm8figSz8kJyLdietRcPwDcfopump": "BITCORN", - "CyyhLNKtLrJ3ckVZkvytBW3TieAaAz7P6qsJLChDNfp": "BIT", - "XsPLBFy59Q3hY59KLAJur8QyvziMF4xUxGTxXqXE7cT": "BTBTX", - "aiddowDsfMxaxPtVmmmnaX9B8DNzM78YypD84dVra7B": "AIDD", - "DR5cCdABB5sZjyVF7i3mestghxGzvHawQk35sP9QFNjf": "BFBTC", - "XsHt1AVPshZpfFWGGpooJ8tdeGknUQVJ9CwhvKrnZrw": "FUFUX", - "bgSoLfRx1wRPehwC9TyG568AGjnf1sQG1MYa8s3FbfY": "BGSOL", - "bgJWGuQxyoyFeXwzYZKBmoujVdatGFYPNFnv1a6ondo": "BTGOON", - "XsvHMmbDcd14DHHW16PkxPGW7ks77ehxUv1E9Zmxgj4": "BTGOX", - "2XEm27fupa295SSXpmD8coakcFpCJn3huMsH5V77ZixP": "BTM", - "MYXqkDYbzr7vjXAz2BapR4AiYRXzoikGirrLoRzondo": "BMNRON", - "XsrBCwaH8c46xiqXBChzobgufRKxQxAWUWbndgBNzFn": "BMNRX", - "ErSZNwsamWjR9Hj2o18GLwVyTkooyGbdptjMjuv72X8A": "BRISE", - "63SRKBw34MJtwKevMgK6GXBJgj5MQFsn8fgrQRvR1wof": "BTSLR", - "ASq6BSBLc9sj8eg33eDyhk8xssq4zSnetrM5zTtUpump": "BITBABY", - "7ePhsDXkpPJs6rPM3TQScpbgonzKUPo9vuQr8GXMMGpf": "FTW", - "9xsF8KqQn7E8cwDS3jL45rZNDQJsCKatHgYH1KV1pump": "BLACKBALL", - "2oCxjpWWEmCuNaXiBSVahUKP1xRMRAiBfCg98YyHpump": "BLACKBULL", - "J3rYdme789g1zAysfbH9oP4zjagvfVM2PX7KJgFDpump": "BLACKCOIN", - "Be6a7UZT4k6yUgQibJczPat8C2qGujEAoje7wY7Pgegr": "BLACKCROC", - "DjgujfEv2u2qz7PNuS6Ct7bctnxPFihfWE2zBpKZpump": "ANGLERFISH", - "i3s1iZgKxLNs39S9JDceHmFtVhJH1WFZHkGNWFUmoon": "BD/SOL", - "8dmHzuMkdssGeycyiJFDhkmeuViLBn4GNdH8w6qkPp7i": "MIRROR", - "Hz5umnpTrCjXNUrZtZAdviEfmXDzmaBZiQefVdb9pump": "BP", - "D7yP4ycfsRWUGYionGpi64sLF2ddZ2JXxuRAti2M7uck": "RACKS", - "5H1VpMzRuoNtRbPTRCz35ETtEUtnkt8hJuQb9v7ondo": "BLKON", - "GyWgeqpy5GueU2YbkE8xqUeVEokCMMCEeUrfbtMw6phr": "BUIDL", - "Xs5ywCCaayf3TFdxFxuJCr25XMoqY6vdfJKkSHE4D6r": "BLKX", - "3jyiXJ9zi4B8j6EXvdH6pCKTJsYHWvzN5nY8Mecbpump": "SWAN", - "8TVr3U85V3Uazkxd5DJbmzdUWaxhQdEGNNGJ7eNTpump": "TULSA", - "bSo13r4TkiE4KumL71LsHTPpL2euBYLFx6h9HP3piy1": "BSOL", - "A1t2UviBYwyfYZDJyKY2W6Td8ritgsCriUDuNaAQN49S": "BLESS", - "BLiFF7FoZrs9AMnZnn8dXn89wybGiFgQtpRDqMsdLQF": "BLIFFY", - "AbeDBXvqGnmcvX8NtQg5qgREFTw7HynkCc4u97xcpump": "BLIND", - "2mESiwuVdfft9PxG7x36rvDvex6ccyY8m8BKCWJqpump": "B402", - "b1nkm6skdrwXoMku8CVx8Fk4LfC2jAqxV3m9VDNjYqz": "BLINK", - "62CsquahdQ3J286G9UTqV6whxryfihdV4yg7kSJnpump": "BGG1", - "DgG9sM56ZcVidBV8bNArQPm93a2rmjzHkrrUntGSpump": "BLOB", - "2zwuFv4tzMrJMA3Gq5nNH3i4ojWPtCkjmGzTMXe4RpMc": "BLOCK", - "6CcuCL1LESQqTznT6wi9kjLPnAZs6b4GM1D1GeS8pump": "BLOCK", - "BF393sQtcNVrMEY3jLDuho5eTEyMjqAfuQ4o1jZ5XcHq": "SOA", - "NFTUkR4u7wKxy9QLaX2TGvd9oZSWoMo4jqSJqdMb7Nk": "BLOCK", - "9JJdWb2y4K1V1aHZJeZdiyScfHWGW3cLF1XNGDd2pkin": "BLOCK", - "BWxe2FVciUbwrCUZQPUKiREBh5LmVa5AiUqNLAkondo": "XYZON", - "F1By3oDsf4YY4WTNAMznXwsBgGt7KBQPFfoMA2BEjups": "BRS", - "FoRGERiW7odcCBGU1bztZi16osPBHjxharvDathL5eds": "$FORGE", - "CsZFPqMei7DXBfXfxCydAPBN9y5wzrYmYcwBhLLRT3iU": "BLOCKY", - "9TwGzR67b7pVjhiDZyHoYk5ynYGLaCG3hezKXK6642US": "BLOCKY", - "GiUgtv3NiUteADLBUg2Sc8Y2c3d3doYLpnZkagZqpump": "CHANT", - "4vKEwZ2ZHmHFuQEE69emXV2Zq1EKeJYVCESsMqydpump": "SPERG", - "6xdfpmgALwfsnFcXUtiwSxdbH8Y7ew16UBaxKEmspump": "BLOOP", - "FJnUYrGfdb9386ybtwjUt3i63c3KxYBANeg2iionpump": "BLOP", - "71AZt4P5kc5vpfTJR1HjNbMzD84G7vxam4DtL3kJpump": "BLORMMY", - "CFB4Ff7W87uN9Gf2DSj63L7prZycJvzQeg1MbGxwBcqC": "BLOWFISH", - "B1rGc4HM4Q6q4nU78ADM7fGguxqiasH53fh6ViDXpump": "BLOXX", - "myrcAs6bpP2g5oGHZ3qpgrfZQAFkbo9KUHdqYDXMjGv": "MYRC", - "2u3ufZ5defxwPXMixvvQpSs68PDNzHTdE52t9Crspump": "BLOXWAP", - "GEJJkUe2MDSsRg64iJBdKSRdsuo6V4kdUzrgyPogpump": "BLUEBERRY", - "CmAtKbUSM2HErtaQmonrxiV7qXo3mDganWw9KEimbutt": "BBC", - "676k7xHRPBxEQ1NsjEKHBtGqRHr3gPev3JBje3VApump": "BLUECHIP", - "DHJVYXsikcimtcVo49FAZqYd1XPYPaXezYhbKArJbonk": "BLUECHIP", - "CWQVqTKuH1iU8zSfFfvaUXaVzLzqU1E8GYU5d6EcGBNe": "BLUE", - "jcztDWkrkxyMorbBVd4Amm7PbGHafCLMN94aAKZdrug": "VENOM", - "VjMa3mzfBgCyKRTQHg8pSuZRKCvKF8zMqVXQMU3pump": "BST", - "kub2QX17qMx6jLuyG5gR4kSmmbiRtvUHt4gxqNd4wZB": "WHALE", - "ERuVbcuTrs3UCQ64391JfrpVdv4JLkqABCiZNujxpump": "BLUFF", - "6h1ACr3LWM1YgVqxwcqEVJtHA2ygT8Z7tutYMy3gbunt": "BLUNT", - "7qpKH1bkvoZPDGbtmjHa9opCGnzfeycD34iuDrJBpump": "BOB", - "bobaM3u8QmqZhY1HwAtnvze9DLXvkgKYk3td3t8MLva": "BOBAOPPA", - "48yNDqabAvGNfnkhadsV1MAvtp44fFDdHBRBdFhvpump": "BOBBY", - "2XYgocKz9MvkNVVyj85kdM2VxsUwrJeQUZVD4qmD4dYT": "BTC", - "BoBBYtpE2kpAJwh5TPPky72KND2cWmtdYa63bqo2yiKs": "BTC", - "nnt8dZU4ZSiPXUdcmku4VcJFmhp7SsNDipzBKSppump": "BOBLS", - "ArRRiz4XsPbLXNCUWb2dqE85hPxD4MtMhec5jaJMcW5R": "BOBO", - "Bobo54AXWLGQWqCK6EZdrBQz4bKJXeBE9ExD1HJGbXed": "BOBO", - "2CzW1Z6zGqvVHTfF9yeKFsgR5Swq6D2ySETXHTiWpump": "BOB", - "2G4i17fv5oCYbPVeEccV4ZZuM1VeJaxoRjRdmF85Lcef": "BOBUKI", - "9EbJs7KsoWqAoUPTtcYf1kVyvydQgoxcvcitSbcUpump": "BOBY", - "DGMARinczhSb64n9ezNxrPLbcfcftYom9xZo94LVK8PN": "BDGSTRAT", - "BqLQq6yXT4i9W2geFiq5aaPks67FywEnXEbdFG9fpump": "SCANAI", - "1YVZ4LGpq8CAhpdpm3mgy7GgPb83gJczCpxLUQ3ondo": "BAON", - "GMEhF4sFXd9PRR9KJo7hyPjeHdcdg5yxTNP22KKNyWvZ": "BOGGY", - "9zxKFBkFVcyh46r6pJNjZTtFwFZMuJFqrXsTDn8Lnw7c": "$BOJACK", - "1KSpDNUbFmE7uB662rCEVVD2oxsW45T3w9EodeX7b78": "BOJI", - "CQ6VXxQYD1nH1SobagiQDbhND3SLEKhqSxHCPaavBoLD": "BOLD", - "6P7emJG6eErTv8KZhxRJDsFUdLxrx4eKs2td4B4npump": "BOLTAI", - "DfMuAxjpKMb1bi3Wn1kwGaA35HMLoBUdTfwzeDybpump": "AMBALABU", - "BjCmA9ZYwJ1BwusMGaSxe4pgaa9gfXTtdyX27NYEpump": "BONECOIN", - "3MrwYcAvGNNtMcHwscSqFQJ3qhMpKwxZCbprYrz1RYWi": "BONER", - "EchesyfXePKdLtoiZSL8pBe8Myagyy8ZRqsACNCFGnvp": "FIDA", - "HnJ1rwyEZcSMWjXQX4XruLFWqmqquGdXn9zJsRakQFex": "BONG", - "9Es9ttvHj9UUHwyy7QLEPBTyJatYo2FbkuHcQR4cVxJr": "ASK", - "HPJALtLtydXym5Tbv6udQCKcXSihnz3B7aD3PNMNbonk": "BONGO", - "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263": "BONK", - "Gh9AcyeZezm5DBvVpG9eSwN1pArtFGNU9q7RzLKkkQ8n": "BONK 2.0", - "6yiDKPDbqWLGAEBkDvVg6UNrKsLsPVkLbA1TJo4KCdzP": "BONK2.0", - "GMLEFvNsZ8WgZuewxVqdT2reNgABdfVPnjWsjmm6bonk": "ARMY", - "BEyp5W9oQosUDD2hPt2Qeg6fuAkNUbnvR6ZJhD8Ybonk": "BONKBOY", - "D3CVUkqyXZKgVBdRD7XfuRxQXFKJ86474XyFZrqAbonk": "BCT", - "CKfatsPMUf8SkiURsDXs7eK6GWb4Jsd6UDbs7twMCWxo": "BERN", - "CmJWm7B9JrqA5145bCqXMzVDbGfdhaJfVCt474Sebonk": "BONKER", - "41ffYTdG5r9oyPqVaKRPC3rpawLwNTGQ1YTTrdnRujg7": "BNKRS", - "28iQookNbPzdd135zBqmPmMbWfy9kH6p8sbz7nU8daos": "BONKFOLIO", - "E3enwdJowhDeRe3f7mMwAZGVkJ5aWEtPUGRusP2Jpump": "UNIPCS", - "6fHPh6b1WrfoS8f6ZhoziVhNBzTs5LCLnAEtnzfMbonk": "BONKHOUSE", - "BP95AFpABStmiAo5En9a9RXXQoqq5xKSfdaNu2ETbonk": "BNKK", - "6q6tqND6J8fPpAf2VNbFYLuXKVgbPC7BUbPjQTyrbonk": "SAVIOUR", - "5Htub56zFC6gyp4UKKjEb21JTSrWheHbNwbxmXedbonk": "SAVIOUR", - "BonK1YhkXEGLZzwtcvRTip3gAL9nCeQD7ppZBLXhtTs": "BONKSOL", - "BgQ2Tj4y4YQume4guQrkK8RWB8oRQtHYtAta1tXubL1G": "BIF", - "oha3mwwm2tqWRouJ2KGUiz4Vz9FCRcPm1fApm87bonk": "BONKYO", - "booe3XGMztBMB9RhCgNodhJQeaaMmhdGehMCVPMGza2": "$BOO", - "9zqre5sRRdFvKqyTyvEd1jcKDRF4g47s7mZSGUnQpump": "BOOB", - "CEvvz6J1NAxyE5uYBdWNqjMVMKCy7wSu5VVaBDN9pump": "BOOJI", - "XseQvfvuDT3KHKiwPNW1gcjfHbFjEMVTV5NJzDdNQmu": "BKNGX", - "VvgeRQNHRHdYubKC9K2GN567LXTzSVcB7mpu7b1VF4r": "BOBE", - "ukHH6c7mMyiWCf1b9pnWe25TSpkDDt3H5pQZgZ74J82": "BOME", - "3dCtyq4JBQJpRq5SX8mzkXFHF7BxQNhS49CfNWy4QBoK": "BOOK", - "7jmaTFBooHkaSrBJDftu3LcK85KPtqWTCaFZCDxQV7ZW": "BOOMER", - "2HrZ5R18H48b8ptL3n8N5zX65zDB4o2cMCHcP3QfSfye": "BOOP", - "Gx2yQqgguqpKwGCrrgx4dr8toTYevczEtGd6B1pKpump": "BOOP", - "boopkpWqe68MSxLqBGogs8ZbUDN4GXaLhFwNP7mpP1i": "BOOP", - "JmMRbLcKgNCu17yHZDAn4strE5NjmWJ4pCeJ7s7boop": "BOOPA", - "7fodN5Vn21kZdGfvA178ZNctqsZ2kCGVRHfFNEkboop": "BOOPI", - "2wP76beSJ8NB8bgFEkZkQCGtWErxqkxTYEGjx9jYpump": "BOOTSTRAP", - "bootyAfCh1eSQeKhFaDjN9Pu6zwPmAoQPoJWVuPasjJ": "BOOTY", - "F5hqdbykXuksp8P78CAZenSvpPShAYKrP2U2MiZMdgFN": "BOP", - "2kATD4v94ahnHyB8rHywAdLUwWAWAiDq9heMqmVEpump": "BOPPY", - "BorGY4ub2Fz4RLboGxnuxWdZts7EKhUTB624AFmfCgX": "$BORGY", - "BdN2AF7h555TiTpxASirVBhG21dwcr5VNCcnW2BJbdG6": "BORIS", - "4jZXkSNgTQKCDb36ECZ6a2aNzcUniGcDeXgTdtM2HxAX": "BORK", - "Fn8UCBRMAAuK9ivmUv3Fzpxz4McnuqS8CKS15jQrpump": "19", - "Bv2ajiWdngTyRFabXCKiwGDJh5SEfJwoRPkkTQ2uf6EE": "BORPA", - "BorpaZo2RdiYcriLJu2GRefZdgQPmv63nA8jsVVsMV7W": "GORPLES", - "5h5YCbybHMpVj378FrxrypY9eXKoy92Vpw9xyru1pump": "BOSSU", - "BYZ9CcZGKAXmN2uDsKcQMM9UnZacija4vWcns9Th69xb": "BOTIFY", - "76SYfdi8jT84GqxuTqu7FuyA4GQbrto1pLDGQKsy8K12": "BB", - "GsKuLQsKCEnfQxuk4icTEQjc11Av8WiqW31CxZqZpump": "E𝕏PB", - "6TcoT3vcexjsMuuLvS9DhBUrVVcoqBMHEppQGQLMpump": "BNTY", - "H2yHLoC24dM5v1Vjh2Poqx7fZ9mp8EfR2MYseXScpump": "BOXABL", - "9cLx6w1fN6b67VxsGxaFaCTgQcNqsA51p4uMWdkgQqmC": "BOYS", - "BoZoQQRAmYkr5iJhqo7DChAs7DPDwEZ5cv1vkYC9yzJG": "BOZO", - "EJPtJEDogxzDbvM8qvAsqYbLmPj5n1vQeqoAzj9Yfv3q": "BOZO", - "BW8ejBnHKPGBZsQN6aoGdR83Vccrw41KD34ph3nfbonk": "BOZO", - "E69gzpKjxMF9p7u4aEpsbuHevJK9nzxXFdUDzKE5wK6z": "BRC", - "evsoXvNK2RG3ZBUWjqwDzoLHWhUjJbWdMrMza2Ppump": "BRAIN", - "8NNXWrWVctNw1UFeaBypffimTdcLCcD8XJzHvYsmgwpF": "BRAINLET", - "APoM2sXUzdRHTkUjXSsdUheX1wPPdP4HFLotmtRNMU8P": "ROT", - "BeMn2NsTi9Pg34MAihvWfqAq2vkBtGAkpQMs5knppump": "BRAINROT", - "7iX4yQ4zTraFSRXEGpF89emA9xGrhgv6jX57dMENpump": "BRAINROT", - "HoBmCAQp75DHEPvhHqttPUuBTj29K5KRoM9naCkSBAGS": "BRNRT", - "GKgNhpLxCEmBSQgiGmPGswR1QU2shSb7wCApC8KbRAN": "BRANDY", - "BQDMYwgnWr9UBcUCvLX67yXriTVe1bkPEiTQ1TzKpump": "CRUMBS", - "8tH8YMg2h1TigY36iPX33UG8dsm7tff13yo9WSbApump": "BRBR", - "FCMvYCULu3EprrFQBVnCuDaii7gdacSCsFHi6uwhpump": "BREPE", - "XsPHkGBbztHCCe1RMhiAs7CxVqoghmyd6zDUfrbfVWG": "SLMTX", - "DxtssVdyYe4wWE5f5zEgx2NqtDFbVL3ABGY62WCycHWg": "BRETT", - "DXBYAw9aQheMdujaLZYnVSpKSK4n8jMS7HfLbiv5RWnS": "BRETTA", - "F6ExBzKdLRcJkCAknQgfbhRXX78EhqoNxPnegJWPpump": "BRETTGOLD", - "FxVxbhxq4xqAjSjVCsYNFHgVr44hF5i5CnSTpgNvpump": "BREWSKI", - "Fv73EXJBRfctJzLVC3P7uQP6er6JU8b4KtDr4LQFpump": "BRICK", - "86Qzt4Dpx51pYGj7TscfEEBbTspsnQJQpNQxodm6pump": "BRIDGE402", - "HaP8r3ksG76PhQLTqR8FYBeNiQpejcFbQmiHbg787Ut1": "TRUMP", - "BriXYhFVy3aXLiuVUfeG6ZMwHvifJLtxSuS5zgozDnAJ": "BRIX", - "1FWZtdWN7y38BSXGzbs8D6Shk88oL9atDNgbVz9ondo": "AVGOON", - "XsgSaSvNSqLTtFuyWPBhK9196Xb9Bbdyjj4fH3cPJGo": "AVGOX", - "6h5QZc6MoMyZL471g2K5WCyQVpBaeh8AhUXGK6fTpump": "BRO", - "CXXed5DrpgNVPfyMzjTMeBarcCe2c9VTBpU99cbJpump": "IF", - "Ga4oZoNRLkZkruJpS8NLwa8DJCwKP9hbTBSNDQZ9V43v": "BROKE", - "DAMqxQRG5FKsnMNXEdBQMdTSiz86b5BziH4p6FS6pump": "$BROKIE", - "4MPD7cGs9746SkZnqhfRigNmpNq17EgTt76YHKC6GEbN": "BROS", - "BroodjAHbyJ5eisR17AZ5rEvZFzACo4Pk4Yx4zm6AnQ1": "BROOD", - "DdEWen5QEDFZmBJLscgwY81mbZLNZ642zMuR5YLVv8vH": "BROT", - "33rVymHHPxfMvg4EHKBRF3h8a37cG7Et3eQWZzAkpump": "BRO", - "7ou4W7LAkm2QyZWrfsCA2U37bbQ1MCrETCYxHdpFpump": "BOG", - "7C5SPivzYtk2umHScjLXN7WZnstLFf1r5TCsMNaTpump": "BRUTE", - "BRUV9DjBeDZRamtWjhzQEH5roosnBApPhfqS1HQ23Xgy": "BRUV", - "HibC3jm1oq2jVzMR2NSX7xdCNnsGLQ13yK8FEkmhpump": "BRUV", - "FtgGSFADXBtroxq8VCausXRr2of47QBf5AS1NtZCu4GD": "BRZ", - "6xbPNG5m1PPJh8ts9B9zxeqgjkjkgdbxxJp8gJqpsEFK": "$BTC25", - "G3T87339h7sw7NFDnYRko9mfeqP77vxnF4eEbQFbBAGS": "BTCDEX", - "BMt3pq4g8ggWWBnd6DJ1jhVyTkHfWjAfJwWW6sRCbQJv": "BUBBA", - "66Vd315STLu1MaiJkgAuryYB5konVAehDkJeqZTZpump": "BUBBLE", - "FQgtfugBdpFN7PZ6NdPrZpVLDBrPGxXesi4gVu3vErhY": "BMT", - "BQQzEvYT4knThhkSPBvSKBLg1LEczisWLhx5ydJipump": "BUCKAZOIDS", - "Asuu49BSxjz4Qj9hkdrwt3GpxdjvYUfbt5rXnsNWpump": "BUCKET", - "H7w7UWLkUs1pJG2tfr6pPz2eyxurga9NigidDk1Vnnzd": "BUCKY", - "7hZmPPkBDYbFpvzQW54sX3DQHQjEVsVcCFRWsvCdbonk": "BUCKY", - "3iY5ZSkRnevSMu4h8nZyywyeY46osdmC1LAosmv3pump": "BUDDY", - "BHX4SUvAcchzxd3hPr1KH4w83tzu9QdbYwjyXdK7pump": "BUENO", - "3HfLqhtF5hR5dyBXh6BMtRaTm9qzStvEGuMa8Gx6pump": "BUIDL", - "AuLFCTA8V8katsgpkFsezQtkHodJby5M4KB2VryTpump": "BUILD", - "5SFjpjNXo8mXPw9Mfx49jzWBhoiqFTsPm4DCKindpump": "BUILDR", - "vsDLJBTQZTFcnvrosoA49Czqy9qZGu4tgjh768wpump": "BUILT", - "2g1Dh2j9AsdpnR5Js24cdtuUEE3KQD1YpHG8NgQ2pump": "BON", - "8RGy4BZUEkW9dc2UEZ3QHYYdas66X63vazdZJezGJw5e": "BUL", - "GuKMr2mAFh4CFM4Qo2LkU6MKS2fmReTGcmu8GSudgos9": "BULL", - "Et2bFLuPCW77hGW2g7UdrboPVuxatFZt4BaCzhCqpump": "BAR", - "GmTXrcQtTvnzJoTJANqmbbBE9FpFwnmHCDsFHpCKpump": "BULLCAT", - "D3DmEyC5p7LLkvEmtQX58EwGk5LmjqRJVNRmULRLpump": "BULL", - "HdzMjvQvFP9nxp1X2NbHFptZK1G6ASsyRcxNdn65ABxi": "BULLIEVE", - "DNyxufTEwqNCgGruJ4W2ue6u1RTFcH61H4sa42LGpump": "BULLISH", - "C2omVhcvt3DDY77S2KZzawFJQeETZofgZ4eNWWkXpump": "BULLISH", - "A9PFmw9Hu8zzxDUoU351pio1E1XWBWBfWnjT9qoondo": "BLSHON", - "B46q6AJ4xRwWtxuWtCu1sk5DVUiEhAeGLPPoTJbipump": "DAY1", - "XsRiRZg9NGkL3WEZZmqnxvxEoyeHG8LhgzMQF2HRE6F": "BLSHX", - "5cBPkTSaQ2UnPG7xLccQHYs3Sv6QFrAnEFgjsgVVbonk": "BULL", - "HQDTzNa4nQVetoG6aCbSLX9kcH7tSv2j2sTV67Etpump": "BUNCOIN", - "B3hgxnFwveJNzxah9rrx5HnoiSUdz2b8kmggKyYKmoon": "BNDL", - "7y1TrdzE1cEeCgBvgdNB9DViMYdQ7UU2FKhnPDLYa7ae": "BUNI", - "2nhjjqSkA8FYCUdJvQhYjbtZdPjZbNo8VtNKTkJ3hncb": "BUNK", - "8NCievmJCg2d9Vc2TWgz2HkE6ANeSX7kwvdq5AL7pump": "BUNKER", - "7XeR2pjfqocWAVmV3fefcCPgZNE4eByV2vgdcjs9pump": "BUNNIE", - "4mAKVgyAP7DGmQA9fcskevDWkpfoEgbnak1w6nTrbonk": "BURKAT", - "GQe8DCQTBkuX5E2sjvwuKDZsjGYhU8k3DN5dkSbQLfqJ": "BURN", - "3aLhoQ8YuyeSfgGzrpnh9WGLozsgt5FNTJe596q3pump": "BURNCOIN", - "AnRZ2wegdibrTSTZZbsF3JxwkGU5EMDYXnaRtSRyjups": "BURN", - "ygPRx1SuXQLqnrFTp2LWKQ8SJZv75Pq7vuXtiDjpump": "BURNINGMAN", - "AVdsFsS45YjGEitb7yQy3XqnNsMVGL3o4cYA53bpump": "BURN", - "6bESwyduZ6hZGSE2S2CVpZvfDUzB5WgfuyK2vZ6yBAGS": "BURN", - "F8qtcT3qnwQ24CHksuRrSELtm5k9ob8j64xAzj3JjsMs": "BURRRD", - "4AcNRnBBz7NsMfkdcLwgaAu2dijxkxuzg9v7P3MgA4ny": "BUSINESS", - "77KhqraBouiU91KRchLPY8DYbVtfsH4nSBgHCd4UAwBY": "BUTTCOIN", - "FasH397CeZLNYWkd3wWK9vrmjd1z93n3b59DssRXpump": "BUTTCOIN", - "1PNnDdzy3hfKaUW6L35fmwDUwDkuCjwJqaBkAeubUTT": "BUTT", - "Cm6fNnMk7NfzStP9CZpsQA2v3jjzbcYGAxdJySmHpump": "BUTTCOIN", - "3dCCbYca3jSgRdDiMEeV5e3YKNzsZAp3ZVfzUsbb4be4": "BUTT", - "58Ghft9TvGNuBiDzy9cFD2Px9rz5cDVZ1XoMFk1xpump": "BUTTFISH", - "CboMcTUYUcy9E6B3yGdFn6aEsGUnYV6yWeoeukw6pump": "BUTTHOLE", - "EVgPUtiE6Fg7T6RY16ACmydX7uucpCaqsK3es3u2pump": "BHOLE", - "5EvPAtkL5r8PhGedwafiTvDDLi5JoqZMyiSQFfUvpump": "BUTWHY", - "28tVhteKZkzzWjrdHGXzxfm4SQkhrDrjLur9TYCDVULE": "BUU", - "8iP5QbqS34FTi2Vdrny6SHicJEM8C5L3Vy1QYT8jpump": "401K", - "FneWQD6Ycu4yn6gGGYK1BawrVu2Pf91B7Q8X5MUkpump": "SLEEPCOIN", - "8mejbHqzKhPZvZ6j3vikLrbi4GsggZ3UJrBcYdGvpump": "FOREHEAD", - "ESBCnCXtEZDmX8QnHU6qMZXd9mvjSAZVoYaLKKADBAGS": "BTH", - "D5xCPJ38YDAM8taHxVdjNZJrLMeQBeWi7Y47xjpNBAGS": "BTS", - "9H6t1ao2XP2ZeNKRo2gQ3ckrxcUTXgjDmSY1ZB6fpump": "BWED", - "5nfDdZFMsDz34y6p1PAm1yN4FpXrGjFRj2V48dgPpump": "BWED", - "ke7MBNmmhXDegLgAbaVboquYiPYb1SagGpSazDapump": "BWULL", - "BYATmZ7ry2pewxW3213sczJYB7ZJzPr921uvcRcJYYZQ": "BYAT", - "Bybit2vBJGhPF52GBdNaQfUJ6ZpThSgHBobjWZpLPb4B": "BBSOL", - "ArGfk9JJ72QETcmckxVjCZ9ApuATgP1mf9ysVHnPU4ut": "BYTE", - "9dnDosD15dKqPTJqvFwzB99HXjEuMaVZr2Xju5QuwzkR": "$BYTE", - "C8PzjhdNVFdP7PxV8YrRXCRghEqw6fr3feAFMfJ88888": "CTM", - "F9TBTkUELa39d8mpVvJDdcE7VrAcEGgzqBy5xoregCke": "CABAL", - "3tbzRgmVg8bnYPizDc3DiBHEBiDUP1cTfLwJZV8gVFNi": "CACHE", - "G4uJcvo5UAJ3fU1gj96e5DjBJU2RDDPx9Txzbjw6Y3LA": "CAESAR", - "6sSKobm4TSRqJuXMuczGdV2BZityP76PGBJJ2ALHpump": "CALICO", - "Cod7wUkjhcwcgbsRMAT6dJuqJGK3U936LMLrNQF1z7ht": "COD", - "4KMTubLvU8pMRffjr6C7Uy22SxXjxSCjkKXr849Npump": "CALUM", - "Axo7inGzj9dhTH4R4LHxC3kxuRq7uxhiuxLqqLZeBAGS": "CALVIN", - "76WApFF6weYWkhCYHrAogDe6YfrGj5uZYvBdi945pump": "CANELCORN", - "AHeSgUwaAqXLXuoidLWeHMeYVH8SgaHB7QjwG3DZZDw6": "CAMEL", - "733Xr5c3PThmBce5uLSnTzKT4LrAWoGy8tvQvcu8pump": "CAMEO", - "CmomKM8iPKRSMN7y1jqyW1QKj5bGoZmbvNZXWBJSUdnZ": "CANCER", - "6iFUKWGDksVvmnSYJUGYnsu168xstni8xJkTF7QrpPAu": "CANDLE", - "A8bcY1eSenMiMy75vgSnp6ShMfWHRHjeM6JxfM1CNDL": "CANDLE", - "3nCkzXUKY5eqbeJ3RAx3rmqqbnJHqMc2WFhvXqSJjRQi": "CDG", - "FF4dN8Qy8NNF88HRgMA3TkbRVZ8PTXWXZCZJb59X3Sbg": "CAN", - "6fYLNrp7ymmNy2kJnqJNFDpbSkyzQdS6i4kwfe5kpump": "MOON", - "7E2iF4WFs5biCtkAVFCBPEdnpg7t2D19VzxjxEPvpump": "CAP", - "EvDWFiEhRWxpJM1wJHFpe6sy8M14KLTV2bNAh7r6pump": "CAPITALISM", - "R2uDbMtmHq5xSS5SserrovdRKdpiqnVBCd2AHLhondo": "COFON", - "Ec2817RwGNA6wjBxTd2HWU1ohB6tfit8tT5gvr2npump": "CAPI", - "3C2SN1FjzE9MiLFFVRp7Jhkp8Gjwpk29S2TCSJ2jkHn2": "CAPRICORN", - "BS8zoc6pmALQnBhBDFak6eFhgGHjpebnHzsxApgondo": "CAPRON", - "JD3S1oqnjG5trRTZXpLmKnS7LsKsppyFa51rPBBMWogv": "CAPRI", - "BCZUX1BMFrRJT4WwAK8YdLNYnL9MF6vP3JESpgXFpump": "CAPRA", - "BfdzpiKdTJi81rVWo1gXjav48DUzo7hqxCY9nxRbpump": "CAPS", - "QdyjMr627PR7NtWdcEcgFmDm5haBVUWEcj4jdM4boop": "KUMA", - "2umQqRyexgfHcndkULDKStmJJ8xgDz7oBL3EfDJNmoon": "CPT", - "D3S1AW1Tj1BbQVCo34D9frJDoD81dU8YRCPhbtUUpump": "CAPY", - "8QkG1NE8mpgzAc3og6UVxeN3S3uRiaQJPP3fzUYNpump": "CAPY", - "7VENy6wCjBChAMGpjPQCPYJDeYJGFr5NZUxk7uQ7bonk": "CAPY", - "LWPihkg8SWDiAzG5TXAsgQuJeSZ3cXem1VStNX8PLAY": "CAPYSIM", - "D6xWgRCSHoMEB5fqPwk3p6Stxirn5ytm2WwboSTTx4oE": "PYBOBO", - "EQs4DEeaSyrhRPM6JPsf2ptLf4EHkcGXB7ybeufxpump": "SAINT", - "CDwKAreA1ipd1hUDBKsfVXVFWqNEeGDdvmZ7RHdiQk1U": "CARR", - "CRTx1JouZhzSU6XytsE42UQraoGqiHgxabocVfARTy2s": "CRT", - "55paRYJNk2hcjjuAKEACiZdaCiYz1okeuWq1cWit9Ket": "CARTEL", - "AFJtnuqGMaj5jAo6Pwxo28r1f7XAXXTSA8q3rG3q8b4A": "CARV", - "FGmUDXqA3AbWfo5b3NUcsvwoUFCF4tr9ea6uercondo": "CVNAON", - "3up9oA3hqCYRoxXhnAeUBfLv3oNrTCyAqL44t82qpump": "CRISPR", - "CASHx9KJUStyftLFWGvEVf59SGeG9sh5FfcnZMVPCASH": "CASH", - "CSCojiafv6WLUtT2AGTHgcfnYiGoTZpGgBRwhvfuWQFY": "CSC", - "DjUeSoWmxfVx3Lq6y7a4kUH8WNaKZNuqEFWxfwgypump": "SLOTH", - "3Lbxn3uPP58u1KEULn3iJyaKNWLgxbRsS1kgcUeXpump": "CAT", - "9jyF1gnzazzmsNH1BxbcfpE8QrBge7sMsqMBfmp6pump": "#CAT", - "CATA4f9X72ErPxZydy11EGk3e5WVVkxrm2B17tfzDuSP": "CATA", - "GmbC2HgWpHpq9SHnmEXZNT5e1zgcU9oASDqbAkGTpump": "CATANA", - "mo7mapMrCsyci5w1td1wgrKPtNeCfjfkKi96DknWi5N": "CATBAT", - "7hWcHohzwtLddDUG81H2PkWq6KEkMtSDNkYXsso18Fy3": "CAT", - "CATTzAwLyADd2ekzVjTjX8tVUBYfrozdkJBkutJggdB7": "CATDOG", - "DFwxYdmfLJsPTyrNJCapGadF58Y74e2TFpVAyhbgpump": "🐈", - "AErxJJxGbc9cZzZoZepN62BNfg5RXns8tmEc3Zpondo": "CATON", - "AcY6RWuWKM5NU7BeSE4c4zQxBZpyU7pTLNF3g5DTWUC2": "CATFISH", - "fESbUKjuMY6jzDH9VP8cy4p3pu2q5W2rK2XghVfNseP": "CATFROGDOGSHARK", - "FP7NvFXQXoZL1nQC5QZtE1bzwgALdxj1TeA9J25epump": "CATFU", - "CkW5tFrQiGVoKcYxxpKNLLUvTXxnpY6qc43TwyUvxYoB": "CGF", - "GVwpWU5PtJFHS1mH35sHmsRN1XWUwRV3Qo94h5Lepump": "CATGF", - "FGf1Us3kqu9AXu2x1yWKfiKE8uSx42ACvRiUrbuAodzq": "CATGPT", - "MEW1gQWJ3nEXg2qgERiKu7FAFj79PHvQVREQUzScPP5": "MEW", - "HodiZE88VH3SvRYYX2fE6zYE6SsxPn9xJUMUkW1Dg6A": "HODI", - "1A3CZ3b8f878LfqpT3stoiDC7C1EDMjda6A1xHakkMF": "CATINO", - "9CvxZKiSrszi7wNRVRnnHnPzBiN2F2soJ1PUpg7apump": "COOL", - "JyTYXrqcYkvRhotAaYBvFbm6zk7vimHc86hwLZzpump": "CATJAK", - "EavJDLh8cYTAnt3QDitpKGMsPL2hq1My5g9R2P6at6Lc": "CATMAN", - "5p2zjqCd1WJzAVgcEnjhb9zWDU7b9XVhFhx4usiyN7jB": "CATO", - "Dnb9dLSXxAarXVexehzeH8W8nFmLMNJSuGoaddZSwtog": "COK", - "6zgbwgsiQkNP6EXHx7gEXqgKGK4RPpFWvW2HFwi7pump": "N64", - "E99fN4tCRb1tQphXK1DU7prXji6hMzxETyPNJro19Fwz": "SOLCAT", - "4HBQm2EhdpUWZkTYxttxNDnsoWi5beRAGWHpjVo8pump": "CAT", - "EnYkmHKm13mLnBgpvHJZE2T6rqi8weQXoFuPccW9ZiD5": "CATTON", - "Ag9TVNiyh2dDbVAzf1fZj6rjPLRTCLGXQzEDwWuZjupx": "CATTY", - "5Fxk572o7JgkNhv4VbHt7DKfvKiBpapGr3fxjGd6pump": "CATULU", - "EVDoPXkWNRMc7fQirg7emNdc17KNqsCyzVi4mWPmDvni": "CATVAX", - "D8r8XTuCrUhLheWeGXSwC3G92RhASficV3YA7B2XWcLv": "BAG", - "jkGKKj3MinQg8nkcWZBid6XxeSnn75Xoy9AqkqLBAGS": "BAG", - "Uh6aMRwUduFFpxXiyvbfBRUHv1cyw5vuPtLfbtdpump": "DRIP", - "G3vWvAaXPHCnncnyAbq6yBRXqfRtEV3h7vExzasZeT6g": "CIF", - "2gkevSAUExkSUwwwj6uNbzwwNCXyzEBkGLg4wAyUpump": "CWH", - "7atgF8KQo4wJrD5ATGX7t1V2zVvykPJbFfNeVf1icFv1": "$CWIF", - "Avb1PBRudW7uUV9MqTUqfZ3EZTDvNkKS63W3wyPseudf": "CATWIF", - "6MQpbiTC2YcogidTmKqMLK82qvE9z5QEm7EP3AEDpump": "MASK", - "921a1GKnx3B4jSEc8M9dzT9j7oQAuioF8ejKQBGNzFQ4": "MELON", - "8XSRFmCAaMU617gv7RTS7j8nQ5MEgBVVmVcp24cDpump": "CMD", - "4SZjjNABoqhbd4hnapbvoEPEqT8mnNkfbEoAwALf1V8t": "CAVE", - "HZqjjeso24PDVdLsVJQyVb8kDnbo7HhXfY1Jane66o9C": "CDR", - "BD3zHHcK6aqz1RVBawMyhp5DfXFaDYaBXzwBH8cEpump": "CEDAR", - "8PMJczfs9W8TDKiNBD85AuqxE8tWACCDeUwxpUeadL3j": "CEICAT", - "CE11ARyXsqrw5ncLfPf6ZVbsqKjofF3CmUnoKNo2giia": "CLR", - "C9FVTtx4WxgHmz55FEvQgykq8rqiLS8xRBVgqQVtpump": "CENTS", - "7oBYdEhV4GkXC19ZfgAvXpJWp2Rn9pm1Bx2cVNxFpump": "CAR", - "2KFZCkfxj1Us8YRQZa5vktSxy3GPzFZvVhWj91n8Fv2J": "CBDC", - "2jUSPvEuxSxJ6nGpUHyXVAcNxyhugPC1iBeNjhTXzAUy": "CNS", - "CERBERR9kg7MQqL6kXJPhNRqD1VwunTau1DDvjSZ14HD": "CERBER", - "ai6rQuLGBsLG2AJiTjRA4ms2n1e9GYi5gtgw1rDpump": "CRBRX", - "7jiDHV9wphDKcseQaVdY8v3qe5CsciCQW5Lip5DQpump": "CBM", - "neuRodi6Saw2cwDpud7FyAcjzqPBJDtr3fDTXE2Fu4j": "NEURON", - "CETES7CKqqKQizuSN6iWQwmTeFRjbJR6Vw2XRKfEDR8f": "CETES", - "HQPMEbkZmwxCscm6wQmDXc5Ywyft7aRsbKdooh6Ldr8a": "RS", - "DD8GJwAZHZZsLEtKnxdWfm8JbBhUj1oVwiGigfPUpump": "CGAI", - "9Wkcek2EZFmJf5L2XmC5rfnNVBrdndbMe6yW8fbfbonk": "CHACHA", - "8i93CHmhcqtCWMvaAdiTngwbQMQRKFW6g2ojnyhUpump": "CHAD", - "CmUnSFZCKUFGs2L8eZGQukAj833Y8r2SiJav55Nnrihd": "KARMA/SOL", - "Akd634sRBJQWKgosKCYDKQdxNuZr5J7r2VZ2Fwxmv6Zx": "CHAD", - "DZd5bxpRezxkeDz18QJ2BzrxhDk6w9go7qAXgMfaLtjV": "CHADAI", - "EvPUFsBikS1B3GpbmVvUWAc4DXaBXD4zLDER9SVSEF27": "CHADCAT", - "3pGyE8coXwBy8rx71zjtHDRrSA5TvKmuRgqwhK3Xmoon": "CHADETTE", - "6f41LGHuU2HVV67ao9GwJ6kv2jNUz9HL6FJPVYcJpump": "PEMDAS", - "9y4zGixmVFGj47RCtbjgdjqWRASnp2HM1X8t7SHoM3HA": "PUTNI", - "RhFVq1Zt81VvcoSEMSyCGZZv5SwBdA8MV7w4HEMpump": "CFXSOL", - "Fo9wJVqWYXEgsG3UKekvK1R7YVewyUGodRfBrmjaBAGS": "CFX", - "CCDfDXZxzZtkZLuhY48gyKdXc5KywqpR8xEVHHh8ck1G": "CGPT", - "LinkhB3afbBKb2EQQu7s7umdZceV3wcvAUJhQAfQ23L": "LINK", - "8SgNwESovnbG1oNEaPVhg6CR9mTMSK7jPvcYRe3wpump": "CHAOS", - "FxUniKYRESjn43wt2uM656kdSCxdqEXEnc9vTkAepump": "CHAPO", - "EwBUeMFm8Zcn79iJkDns3NdcL8t8B6Xikh9dKgZtpump": "KING", - "cnc6M1zXLdrGR5LAQVcaJDfgezMiVWNtGQsVy1Kondo": "SCHWON", - "22smSdhoAo5KhmXx9Y6WoSSgKtguzaJ3EFJSMW2Tk7Pj": "CHARLIE", - "DBpVGmVbMrw2vEbkhBu2cC3MvEg63opeRHdakVMEpump": "CHARM", - "4nKvQ6gMrTKH17kr9kmHmvRzA5G7Jd7xwxyCyr9Xpump": "YARNCAT", - "9EwutmiMiLHoH5hj5aCLMDJUdsLZVg926ZmDdr8Ppump": "CHATTY", - "Bhu2wBWxfWkRJ6pFn5NodnEvMCqj9DLfCU5qMvt7pump": "CHATOSHI", - "Ak3ovnWQnAxPSFoSNCoNYJLnJtQDCKRBH4HwhWkb6hFm": "CDT", - "GLiB37nqnPDghvVHFS9CJ21c5BDUQxSJS3BXBWduVoPm": "CHEDDA", - "3FoUAsGDbvTD6YZ4wVKJgTB76onJUKz7GPEBNiR5b8wc": "CHEEMS", - "FwBixtdcmxawRFzBNeUmzhQzaFuvv6czs5wCQuLgWWsg": "CHEEPEPE", - "AbrMJWfDVRZ2EWCQ1xSCpoVeVgZNpq1U2AoYG98oRXfn": "CHEESE", - "B1KrhWbacPi3tpjWqnsbjKQJEkp3RvWppGxNzUYBpump": "CHEESE", - "3BeJ9zCgQhaqKMu2HgKJ79yQBChD1Pf3hPwRX44fpump": "CB", - "9wV5ix8WSWLpdpLe5BSSwUJhYptShHfiwBxdYorBkFqi": "CHEEZ", - "h415SDExtTrKXwmxYn7SyAV8QTpiVJojz9T3G7kpump": "CHELON", - "BEsnQvkfeaQ3G8qbeN5FmUnsQUqXzXW5i83Hv3syoTTc": "CHENG", - "7JofsgKgD3MerQDa7hEe4dfkY3c3nMnsThZzUuYyTFpE": "GRIN", - "7tgKziACteG26VjV5xKufojKxwTgCFyTwmWUmz5ondo": "CVXON", - "XsNNMt7WTNA2sV3jrb1NNfNgapxRF5i4i6GcnTRRHts": "CVXX", - "y7WrhyUxATe9xMmdQKGp2pcgZYZPie8wsZuCV1oUkcj": "CHWY", - "2zGQGkLnQgWLvfSiWTXnnxdMA7fiP4ah6hyJPQQe3Eoe": "CHWY", - "8J5e2FPmBJ1subEUeVkELpeBZv9aYUrMmfHys7sREeXr": "CHEXBACCA", - "6dKCoWjpj5MFU5gWDEFdpUUeBasBLK3wLEwhUzQPAa1e": "CHEX", - "UrAE9vVdrWxncikcCRp7TgNqEsArFtP22iXzH7gpump": "CHEYENNE", - "5aUemPEExdpnRYwrHTsALeQgDFda63dAP5bNnSdgbonk": "気", - "HfXLMuSCHvsuYHRLekyW88oiz1LJvRerKh4QH1y5f8xk": "CHIBI", - "FZRh6uAar3gEJb53ruHNjHibCPqiGHghfGiSRVeFpump": "CB", - "CEeeVEznH4bpZDyNPkR9BhrkrKHk8Fh3j794BKEQcRF3": "CHIITAN", - "8s9FCz99Wcr3dHpiauFRi6bLXzshXfcGTfgQE7UEopVx": "CKC", - "J252vEBJq8iABpPqAz9M7AYn6y6F3FoEssjXwF81pump": "CHIKUN", - "Fy1RLA8gvudCj1x4tcpcCDRWG1jfuYHT7zpWKP1svJam": "COTS", - "GPyzPHuFFGvN4yWWixt6TYUtDG49gfMdFFi2iniTmCkh": "CHILI", - "2pb1ny5NA51GbBFNGfo1XYyUpsAacuSdmFRByx6upump": "CHILLCAPY", - "CSr7HhUyBxatPMuTqAs3Z33fwQ4dx8cNAArMert1pump": "CHILLFAM", - "AH9oMYaNWwwh93rthazxXcJB7kyFsLgZMxDJxbwpump": "CHILLGIRL", - "Df6yfrKC8kZE3KNkrHERKzAetSxbrWeniQfyJY4Jpump": "CHILLGUY", - "Zdq34g8CKSMTjNrcudRWCP5P6UDXHaWgqCfxPGvpump": "SUZAN", - "GkyPYa7NnCFbduLknCfBfP7p8564X1VZhwZYJ6CZpump": "CHILLHOUSE", - "Mse6hcdxMWgQgEW7AcidAYw2G5ucW5fC8XK3NWYpump": "CTOAD", - "G36mpCk6NiSqSXxviwc3FYtdvNLFkaniZ1pgsCZTpump": "CHILLMAS", - "E9HkGyDrjJnDDQek2dajCZJ3CMEMBiPc8Q52UDRmpump": "CHILLT", - "CkAtD6EBUjryNJfX9Y9MxC4VKoXUrZG5Xep7juDPj3aY": "CHINAU", - "J3FnBoPv9vLTPi2Lj1S4NFRaWoEDQ47Sy3E4ZfrVBAGS": "HATON", - "FkwpBGmeZpkuZmffZiAJ3gKraw63i3QTDrze61N6pump": "PENG", - "FLrgwxXaX8q8ECF18weDf3PLAYorXST5orpY34d8jfbm": "CHINU", - "chiPiQTvkQ7oPtAD7YLQaEeHmPqXCa2wcRQdwFNneTe": "CHIPI", - "5owVsVFSHACQuippFYdLp3qWRobp2EGcwxMmsr6ondo": "CMGON", - "3T9mBXQzfAn8Rf1LKDutvGMTd1EruJbFh3cM7MSMq3Py": "CHIPPY", - "7kAux1bLGB68VzisQaaXiNKg3sjP89uDDotpuKmTpump": "CHIP", - "Cy7rUZ5rK2ukitsAVVfK5Kn8t35TR417RRuURPWoRBAF": "CHIRP", - "J95PxHUEytzTGbf9DpLpyPHXnUTWN4FbRk2Jnc5nYKa3": "CHITAN", - "BYhskqqbXdhEydYY9NAfYopn43LsPnbb6KvcTAKypump": "CHITO", - "DFy12AkbxKnR2s2gaYz1AvxgxqGDrMEjjzK1GG3Ypump": "CHOCCY", - "EVrGfAj99Xr1NjqqZv6E2msVKZVmhGaThHhcZXzrpump": "CHOCTOPUS", - "59McpTVgyGsSu5eQutvcKLFu7wrFe3ZkE2qdAi3HnvBn": "CHO", - "EngayhhahJGPZ4mqbnDQtVRnhjjf1uoEmbpevBVFJdLM": "CHOMIK", - "F67smWaqebM2zbk1eofh9ihF1vNaDPD1FTA6EGVDLwWN": "CHOMP", - "9tcChCm93yuM93GDiwnERZQrD7wgyU21pHPRFmnxpump": "CHONK", - "CdNWibyKcLUJKvzj41zWtnRsWPiWexbgConijXaUbonk": "CHONKUS", - "H7ed7UgcLp3ax4X1CQ5WuWDn6d1pprfMMYiv5ejwLWWU": "CHONKY", - "2MwjFE1zbXyNKw6VjzGWa3BhPtFcs8htuX2xwRAtbonk": "CHONKY", - "361pUXTPohPEgcnCVm7DUvNziizK37gVxLN3cw9Vpump": "CHORUZ", - "FEhKKF3xMEzgHkBHRGJArdQ8TpkCX9KQD6DhJRMzpump": "SHIRON", - "X2h2pyb81yWFw6fFCF864F9sYYs3d7us4ZHBq2pFSnf": "CHRIST", - "BJkgs94YDeAhSjXRSMestfM4CgMQ8L1m55NSEKjrpump": "XMAS", - "tGSHqcbojd4uSmirz9iXHvuRBBFwbHaDjSatbuupump": "CHRONOEFFE", - "4gBDhgCqTtzyJocskewXARgcLoAfSxkmmoUwwfhbpump": "CHUANPU", - "ELST1oAp3czkRRRWGeTgttGsEqkD2qjDRJXR4Asmkk5y": "CHUCHU", - "6yjNqPzTSanBWSa6dxVEgTjePXBrZ2FoHLDQwYwEsyM6": "CHUD", - "BKUFQTnhsBnzZFBq3s9V9bFwi4EVZd7KXBP3GqY1pump": "CHUOI", - "A3t817aK6XkhqQA148bjXKWJMhBmP9t2d9caWvQNaHQR": "CHURRO", - "exRqAuXs967fDkymNXo4eqZEQihEubstBCXPXxwpump": "CIG", - "25EDYHRPkywy4X8aE1s6iNTztGRbsaRMMwBEerompump": "CIGOL", - "Ciphern9cCXtms66s8Mm6wCFC27b2JProRQLYmiLMH3N": "CIPHER", - "WNZBSkNBNP3Ct1pcFn6Fu4sZQFhnu48EsM9voCEondo": "CIFRON", - "3qAR4mq5X8WTuyqDeie2Mj9wGrYVb83TiGdkEgYbpump": "CINET", - "6xHEyem9hmkGtVq6XGCiQUGpPsHBaoYuYdFNZa5ondo": "CRCLON", - "5fKr9joRHpioriGmMgRVFdmZge8EVUTbrWyxDVdSrcuG": "CRCLR", - "XsueG8BtpquVJX9LVLLEGuViXUungE6WmK5YZ3p3bd1": "CRCLX", - "7DWcZE1uVc8m2mf9pV8KNov28ET7HsvHkhrhgr9ondo": "CSCOON", - "Xsr3pdLQyXvDJBFgpR5nexCEZwXvigb8wbPYp4YoNFf": "CSCOX", - "PjtfUiw6Hwd8PZ94EcUw8mBSYxp7SjjzSLeNTDKondo": "CON", - "ENJpMVwQ5Cxi4SNtSmAX1ZTm7KBHPwQ3WaRWgRyvaLB8": "CITYBOY", - "6WBynoJreWH4dfayG1Qzrq5PBUstDMUkR1c7mSemBAGS": "CJFT", - "3KT4KA8yJpAVXQ8xxbYzGjBCYhcAtxCsHaB9iyWeyyNU": "CLAIRON", - "HW5SN5NH9jsUbYsGfTHfzfkhtAdtTiztF4qzsNQ8bonk": "CLANKER", - "CLaNxaMQ3S4gyiwo8GeJLE7s8uCjeEzv5DWhg9ptC6to": "CLANS", - "9b8jL2wcVjBFpieC5TUR76BDJ6sW8Eghd3fyq5VJmzir": "$CLAP", - "3oaRkLHm94Y2fisjNPwkcHV7iUjTMdh5cSQyGarUpump": "CLARITY", - "HaL8cPcEZgwLaCbwyf6aAPh7rhw7iHck22zFXSNjpump": "CLAUDE", - "6b1Gj8h5N7TSZv3HwnqXkGShZGZJ6GCrfRkha3Djpump": "CLAUDE 2", - "2xh1LVN7yD1dBGfrGcKz4BCTvcgrEyjouoHp9Nq28FZB": "MCP", - "DSyAs4y6siDcC3qYaz4yhhXZSu6ShMgjYvrn7xD1pump": "BLOX", - "2TsmuYUrsctE57VLckZBYEEzdokUF8j8e1GavekWBAGS": "CMEM", - "FJPoRjKUkE4BP3cULAKiqztBjTxRG3qcRTV9SFf7pump": "CLAW402", - "C9jkxMsjYhaZN6CiMqEbUfEc87kqf8dN15xmwTUWpump": "CLAWAPI", - "ELusVXzUPHyAuPB3M7qemr2Y2KshiWnGXauK17XYpump": "CLAWED", - "EahUihCyvsJVg8wWafXc5ytxaReFXms514wKvmBQcLAw": "CLAW", - "739dnZEG4yaBWFsY8L8ZwrfhGG6dhtCSercW8Umspump": "CLAW", - "77UrDzChDJmSQbQDEjBGLmaKqyxbWiEEzSTtEWwbpump": "VAULT", - "6N5cSpWghA9p5Q8PRNkZnvh3aZB2gf3regzQXcnZYaMo": "CNZSTRAT", - "Xsn3H7ACEpSF2ULxeiD6kW4jRZXpurh8ZPttyfoS56W": "CLSKX", - "AeXrLftu8chuY4ctc6oDeG4dUx6Yr4aqeakUMFNvACdg": "CPOOL", - "7Gg6J1nkHLVV2TMqhVGHS1BrYycDmLoux9R3GSy2pump": "CMD", - "Dh69esKUndpk3yAG2Cg4VHMfYLaAHSeu6kuLRGv7pump": "CLICKME", - "6tSUENw1SsuffbBdBYmv9iP7c6fq1jQ8wPJyS6wcpump": "CLIFF", - "9LjLmk78kDbpsR18kYcdbEJe9yWALwkkSWaXA76Epump": "CLIP", - "8WHMgk4kRdZaBDZkgyBpb6bw9XkZzJ2jXCv4gj1zpump": "CLIPPED", - "GKjAe1bQXXLoEitJYSuyw6qt97tTVoKkGEgWPEo6pump": "CLIPPY", - "7eMJmn1bYWSQEwxAX7CyngBzGNGu1cT582asKxxRpump": "CLIPPY", - "3g7Lv7KKCXH6Hyb2vUKYmAFJvKgspWD3CER1NxAY71AR": "CLIZA", - "BrZmDQwbdLXme3uaea8znLhy9Bb3fucMJmcFBuRPpump": "CLOUDY", - "Fxb2Spt5fxoRZNeiMro2RFvr5vDKAggJYdgZVvJvpump": "CLOUT", - "Cwq13pofafd7KNbWBabULgt7QC1w53NjiPgES49Ypump": "CLOUT", - "AWGCDT2gd8JadbYbYyZy1iKxfWokPNgrEQoU24zUpump": "CLUDE", - "E3yUqBNTZxV8ELvW99oRLC7z4ddbJqqR4NphwrMug9zu": "COAL", - "CuTmqTAkdpTufMrSM2sUHG1SWPHZsaZxn1ZzfEeHpump": "COBRA", - "8WnQQRbuEZ3CCDbH5MCVioBbw6o75NKANq9WdPhBDsWo": "COBY", - "cocvP5K8DsDYbJkRGasSg69xWFenrVVdBLLKjJKPJSo": "COC", - "e6G4pfFcrdKxJuZ4YXixRFfMbpMvgXG2Mjcus71ondo": "KOON", - "XsaBXg8dU5cPM6ehmVctMkVqoiRG2ZjMo1cyBJ3AykQ": "KOX", - "DNeC4WPqdjcptsx39nfM4bkpQ1zeccAD4XLpPBP1pump": "COCK", - "APXsvXT37zZocsMtdzfgGrAFzYJJuwGYQCUzuHxwaW4i": "CDX", - "125FKUxLgRuVUkiJZoTvcNSuXhDJ8Hsaw8HBoreaSPL2": "CODE", - "69LjZUUzxj3Cb3Fxeo1X4QpYEQTboApkhXTysPpbpump": "CODEC", - "2Se6tpGxjBPvuvk93LphyvUKVk7M8J5LMQkKEf4Gpump": "CODED", - "H1XL8qRsthUTZg2XpM8YmkfYuryL9cA7wywyYGsdpump": "CODED", - "BqyjVnfh4WaTXC23wyNnB8EMhJXUZDqJKvZnNagY9Exp": "CODEPUTER", - "DjAbfe6EjhvZs4RAajYs8r3jcC1cFqqK4beTfGS7Lidd": "CDR", - "28ECNMKBj66GMBJxq1c3VyzJiKY5gDp2sEYc92Ybpump": "CODERGF", - "85Yf3z6zwgLGQLfxww7KoHGw4sHpx4iki2P1Ci7k7gqZ": "COFFEE", - "8s5X3yLEx2rx8BxGT5uSwR9Z5FP5T64VLr4kkPStpump": "COFFEECOIN", - "dFVMDELpHeSL4CfCmNiuGS6XRyxSAgP7AwW266Lpump": "COG/ACC", - "BXMkru8ded26p71gJ3AMMwJmwZaYYfQjRo8vbZzondo": "COHRON", - "9LjvTxWgtoEVdwRucY9hJLqmTcPnCRxoDiwbpzRvpump": "COINS", - "zDhCPQhQC8GPV2N6n8TMBcWBQehYkMHBuBZMzy3pump": "COIN", - "C98A4nkJXhpVZNAZdHUA95RpTF3T4whtQubL3YobiUX9": "C98", - "CUSDvqAQLbt7fRofcmV2EXfPA2t36kzj7FjzdmqDiNQL": "CUSD", - "5u6KDiNJXxX4rGMfYT4BApZQC5CuDNrG6MHkwp1ondo": "COINON", - "cbbtcf3aa214zXHbiAZQwf4122FBYbraNdFqgw4iMij": "CBBTC", - "Xs7ZdzSHLU9ftNJsii5fCeJhoRWSC32SQGzGQtePxNu": "COINX", - "3gNjMbdpnLDXNsTPYBA7xvvk6W5NQ8XQyEzkTgZcpump": "COINE", - "6CzvM95SQABhdjX4taXdpXYArz5daqcin4iM7CGjNwmW": "COIN", - "CoiNdEjucrH1268Q6eSdkF7N9f6ZFq67LQjZRPxiebp": "POUCH", - "74eSRkPotxr8VjiZYKFnoYr9JDVEycWkrzHhFpuKmoon": "COLDPL", - "JCRiQKaujJjAjfVH8CNo4htGFMuTTnZtwvasVwneT7MA": "CLS", - "3oEHLDg8VokqBM37u7ohkbKUKcdPE8sT8Btgrxovpump": "COLLA", - "C7heQqfNzdMbUFQwcHkL9FvdwsFsDRBnfwZDDyWYCLTZ": "COLLAT", - "AFDzaLz3cQZNWjnWbyq2q81TLVTPbHTbfkj8qKqTk74e": "COLLE", - "CARDSccUMFKoPRZxt5vt3ksUbxEFEcnZ3H2pd3dKxYjp": "CARDS", - "A15y4AUrNwxZu9qSiXuZjVsMDLUGEEGaAc6mEAfSpump": "COLLIES", - "DCmeU6DAdNqMNamxw4Uw4Zp6rdVwwRcJDDpbPVJCULT": "COLORS", - "XsvKCaNsxg2GN8jjUmq71qukMJr7Q1c5R2Mk9P8kcS8": "CMCSAX", - "9PR7nCP9DpcUotnDPVLUBUZKu5WAYkwrCUx9wDnSpump": "BAN", - "FpBL8Ta2XGw4VrpDrw8EH1Qm3mb1o4brHweASeszBAGS": "COMET", - "H1MHqq1dsExxeuYTd8JZzM51z33RvxjuM5sx2oB11WJ": "COMMON", - "3R4WF1RzaZNe7tyJCdYcwuFbcADrfQbHTAA8Xyribonk": "COMMUNITY", - "4bY7N2hPq4aHQJwbhawceNqS5ksyQAayiLEsjNMwpump": "COMAT", - "4chpewx2GE33XeBowzN1KA46KrR4gYvWoAfUnaUcgfZq": "COMCOIN", - "2s9MXSwWLYQGycPrno3KaohfZNriPpYoggcMeZXCpump": "COMMUNITY", - "Hv69wUkD225TYq111eAar9CtjhNpzBTRFpHkpY3pbonk": "COMPANY", - "6JvpHB2ngA7br2V1JoC7nU8J1MS7Zf3MRcGFcGaBpump": "COMPANY", - "5Wsd311hY8NXQhkt9cWHwTnqafk7BGEbLu8Py3DSnPAr": "CMFI", - "3jiqwBQVRC5zRwHyqvnkQurebJ5RNxg3F5fXMwaxgkv8": "CNGN", - "J3NrhzUeKBSA3tJQjNq77zqpWJNz3FS9TrX7H7SLKcom": "COM", - "7XU84evF7TH4suTuL8pCXxA6V2jrE8jKA6qsbUpQyfCY": "CONAN", - "CQvadZTR8vikRqqwyhvYV8YpdfCRjUCGyQwCuY4rxBQt": "CONAN", - "3cL96frxAmNwpPHFqHCuzXscGvdpzmaACjbx8sH5BAGS": "CONCENTRIC", - "G8LfyGVsjsLzetJ5RWZVAhMo4H9cb58ET1Z6gEZJQdPM": "AVA", - "WCTk5xWdn5SYg56twGj32sUF3W4WFQ48ogezLBuYTBY": "WCT", - "X68p9qTpEMkR1TLpXUP2ZJo8PG4Qge2Y2ZLdjA2ondo": "COPON", - "7NWHifsBnn9DimUeNnsHdEXkTZhXmJTiXxcCngBondo": "CEGON", - "CobcsUrt3p91FwvULYKorQejgsm5HoQdv5T8RUZ6PnLA": "PEOPLE", - "bXubNUrG4z3cKi2yVTkYEDTfCLQqXKQFxEtzUNzsBLV": "CLUB", - "B7zNKphr8fjczB71oi9uF9pCd5XSNJvBn78TVF7kpump": "CVGT", - "4AKYKa4JLKQau7m8B7hmFdN8ur1SvqUDqJt6D44Npump": "CONVICTION", - "KnrmAHo1wW2fGqVXcSZSpo6kmiKam5vNbDByrURpump": "CONVO", - "7z5MTBLrz3Bj6rWUj8yVry6GvuTP2eHMcbi4k3rycook": "COOK", - "EKgUxSq2ucFYwaoGfwz3sUpqDepky2vSfpr7jCEGpump": "COOPERINU", - "8HGyAAB1yoM1ttS7pXjHMa3dukTFGQggnFFH3hJZgzQh": "COPE", - "DMwbVy48dWVKGe9z1pcVnwF3HLMLrqWdDLfbvx8RchhK": "COPE", - "o1Mw5Y3n68o8TakZFuGKLZMGjm72qv4JeoZvGiCLEvK": "COPE", - "2pASaZVMQCusTpUc6MdfFBXSML9yKeJQZGLf8L8RyYwQ": "$COPPER", - "HWMYPcKxRoSHyBGEA5SZfFpDkKWBeiXwx2PYoxTTbonk": "COPPER", - "61Wj56QgGyyB966T7YsMzEAKRLcMvJpDbPzjkrCZc4Bi": "COPPERINU", - "C3VLBJB2FhEb47s1WEgroyn3BnSYXaezqtBuu5WNmUGw": "CPERR", - "BM8b4U4cyXCieBRPDAAAygiQwKQemEJntt2yWJ8Jpump": "CC©", - "CoRAitPvr9seu5F9Hk39vbjqA1o1XuoryHjSk1Z1q2mo": "CORAL", - "4FdojUmXeaFMBG6yUaoufAC5Bz7u9AwnSAMizkx5pump": "CORE", - "HQasg9aKwQzHvAQn2zD1mhVmht84BACdeycJLYEwBAGS": "COREJS", - "XsFnZawJdLdXfBSEt5Vw29K5vdBiHotdPLjUPafpfHs": "IEMGX", - "XsBGEXxbBcuu8Nrokj14G8v4ezT3JYWWLneTzXK8t6Z": "CORZX", - "BfPGpgNyxe6rjAru1EJarjSBAcCABuMF5L32v7nondo": "CRWVON", - "79F32BvHBE49gPsvypYTGzcpWGvt66mgvenQow3mJjXu": "CORGIAI", - "DbPCbgJ7a37VgUMb17DF9vRucoL1ZuG5YrqF7y5Gf7qJ": "CORN", - "ApqLH7ovTBeEjgBMXH8udnBttwR9k5JEv9pNnVQV7Xsa": "CORN", - "92BJKpAnkoaTh7uyt5TYAFPu3CgBdZYNQKiF6zo6pump": "AUDIT", - "CortexFv3fRcLKTgACr7aLqckGh5eP7TP3z9JHoKqMc6": "CX", - "9AyLH5Puifc7v9MkTgA36JabS4wiVTEZ3aEPeNoTpump": "CORTISOL", - "CoPRYLGHc7Qadere13xSPhRvgwwStCZn9dHpBZQ7pump": "CORVUS", - "Av6qVigkb7USQyPXJkUvAEm4f599WTRvd75PUWBA9eNm": "COST", - "6btaz134wjHkR8sqhAYrtSM6tavftfxnRvnyMd8ondo": "COSTON", - "XsiRaD8qZm1P3KViowDYFS8rZ8BYT2g7ZEeyztthRye": "COSTX", - "2TEy8cA4bmYSrA5Y2VbifUuzbeS764K6YtbibHAspump": "COT", - "9civd7ktdbBtSUgkyduxQoHhBLtThf9xr1Kvj5dcpump": "COULD", - "FpZWLF31ymwJQNKwdhxAog9HwFEJGWjk6JqQUWYnuHry": "CBTM", - "NKyzy31w2J7odLb2CW3Ft4fpKXkW3LBt1pvpkVLondo": "CPNGON", - "DcU8uHn7abXgYL8AJUK3t8FckeAXrNcGYYBK7uBFpump": "COVE", - "BgbnCT47z1uhPmRpG6X2FeM8dkmuP67MALFzXGgGpump": "CRME", - "Gst1mHM4sqEgUeZVNLRaDhVZBKngsnpch4abiVyGpump": "CRAB", - "2o86pbkvCVru88TLtsBthcHHn4aR2yEUapPKbYtmpump": "CHEWY", - "HdiyGfMmWk3tSXKAkKcY8oxoCXxABNjfbhxfiJA8pump": "CRK", - "8gRCdUnRpQ4XcwfWFzJGymX8ze6rEk8ZcfZME17Ypump": "CRAPTO", - "6JGSHS9GrE9uG8ix63w3DPMYHrgrJ6J4QyHbBhAepump": "CRASHOUT", - "2ncKzZN6b2GbwWZVxQ4Q7nRQZbzqaR61MXrp4J7VE4Zj": "CRAZYBUNNY", - "E7ENfG7EA1oGAtgZiaL8YL3THoSwu72LbsYmpN6n5ZXw": "CRAZYPEPE", - "6EEWHJBFbF4jQtXTn1NyGoA7WawF7W8fy1GQyvhqpump": "CSEED", - "CRGqUDQ4JQKx5pTqWHRpwiA95fmpuQe2ir92eBi6hgmW": "CREATOR", - "65svCEvM4HdBHXKDxfhjm3yw1A6mKXkdS6HXWXDQTSNA": "BUDDY", - "VTaqS4G29U5UUfYbXwzPjK4sDPcswuKZfMY34DbsGqb": "CREATOR", - "H94yBE9hRVFHEcyqtbP4tW7YGRqBUA6qFQrDHMZTe56J": "$CREC", - "Fg4TqUxx6E9L7Pya6fMUGzTD4s2jwSMzjrWeeBHvu7ZC": "CREO", - "DXJTZkJSCHw3XRrD7rMkw5HDvDEbq2fqt4CCWd5Wpump": "CREW", - "LoLLZBUGyTPNWyFF48KbcmkZGc1ajwaFdWoLhF2nBfQ": "CRINGE", - "AaV3hry7G3kLTAmM1dKwxDiqfGuPCtuSvvQCcozRpump": "CRISP", - "5V9qpFo8NMFyhYHbrqY2c1TJrvzZSnRJAdj4ryPVB18n": "CROC", - "GvcNXdSehfNSNyhDVDj27kc459LzFqWozt9CSJywMy6r": "CRODIE", - "5Hx5iXtTFDGNpXPyTMbwMvkH6Uh3bycUz6Atdmzmpump": "CROISSANT", - "CTAyri1T4d1QRfnmtSRpEsPS5tyH6shA1WTskhybmrMT": "CTA", - "AZGFPtxBRbnZtXw4hgQF4BuSmWK3EhUg8omdUA9DEL3Y": "CSW", - "FWMad1gzugDrifDTxUYFxkAH8SWWSoDT2BfZP8Pnpump": "CROW", - "cdKfoNjbXgnSuxvoajhtH3uixfZhq1YXhQsS1Rwondo": "CRWDON", - "Xs7xXqkcK7K8urEqGg52SECi79dRp2cEKKuYjUePYDw": "CRWDX", - "GDfnEsia2WLAW5t8yx2X5j2mkfA74i5kwGdDuZHt7XmG": "CROWN", - "CRWNYkqdgvhGGae9CKfNka58j6QQkaD5bLhKXvUYqnc1": "CRWNY", - "CAW777xcHVTQZ4CRwVQGB8CV1BVKPm5bNVxFJHWFKiH8": "CAW", - "DTEqTxxGFn3SZ4C8tNP35X8iegCCgCBrX974WFSuYVZh": "CROX", - "6yUoLKSJtLejPNbHfk6FaUBXLJHVkjdqgTtgqaYSpump": "CROY", - "Hz7MeU72BNF9rCWyUFAwKTyCcjr6qsJm1jwYehnqjups": "CRUMB", - "9iVJR1tjDgNHw88xtXXGaU2We2fR8qEm75ZSgztqDkag": "CRUNCH", - "HkBWJJiaUW5Kod4HpHWZiGD9PQVipmMiPDgiRPcNpump": "CRUST", - "DkuhDqqxnXwECj2XGhLyrZbWuuRgqyFvU1W4xFJ5pump": "CRYFI", - "Ep5tnKTSkLDeQ12ctS3RhQNcbcApkcyJcGPgHh4dpump": "CRYING", - "HfYFjMKNZygfMC8LsQ8LtpPsPxEJoXJx4M6tqi75Hajo": "CWAR", - "5e6Y8yJ56i7nFwiks1Bqs5BXyN4DuvwHvdzoGYxjsHsn": "CRYPSI", - "9YZZtRDqd9kiJMqgZ8XmFaE1mfK2T4kNpYgW1Nfn9rYV": "CT", - "12You7a9CKuEH6QMdjqX2Wuvxo8bkGjK4YEDngmiGsub": "CRYPTOAI", - "BNeaFbk7DRjMr1UhGuchRGMKvk2Z5bU9W8sNpn7Wvr9Y": "CBC", - "Bksfke6KWgpftafmbR3E9J4wHr4bBfKDrztXN6etpump": "LARRY", - "CDCSoLckzozyktpAp9FWT3w92KFJVEUxAU7cNu2Jn3aX": "CDCSOL", - "4ikwYoNvoGEwtMbziUyYBTz1zRM6nmxspsfw9G7Bpump": "CRYPTO", - "44fvUC89PJcFDNFFXXevaqMa5BXXCQXNGJRfTMiEQhBE": "LAI", - "o86kpPZy4Vo37Pdg3SBkmzgTNhkAM2sfghirvgSpump": "JOKECOIN", - "ESsCdmGyPK9sostFbWV5iM6owxcqsGNfBQaj3xkAkREV": "CRYPTOK", - "CFhgTUXw7of3PYdbw3YrPeE9EVzVGo435i8FEmd8dr6w": "CLOOTS", - "Eme5T2s2HB7B8W4YgLG1eReQpnadEVUnQBRjaKTdBAGS": "CRM", - "EqQFU4AoRVKJjQrpshmp89YxHAgNecCpJdMS8PJLpump": "CSR", - "6fUwECXzRQeh2wYuTg3xeQHGt4wSbiUbsdd1PYw3pump": "CT", - "xGPMieA2M8aUrfp7MMsioSLanaXWwS7rVxYcvrMbonk": "CT", - "EiUKEQXENExJjjuU2ZPVK3RgrJ1gDpaQb5w3UxAwpump": "PICKL", - "CudisfkgWvMKnZ3TWf6iCuHm8pN2ikXhDcWytwz6f6RN": "CUDIS", - "CwxXrctZ9cR4RzFy2vzZUcbCgG2TUncwwjS59Bymbonk": "CULT", - "6Q9cTZABHC7cGNXWjBwPQmGa2AH8eyg1oVybANV6pump": "CULT", - "4J4W2tdq8gLLEdrq62HqCAbpPAci8SSQdSVvy6uLpump": "COPE", - "9Poxe5aDkt8ggFWvcYZ9AX8QBBAAdgW844cgooNCULT": "CULTS", - "3zu566b1F47koogyGwwmcCDeo4dnE8zaYCtWYmBfpump": "CULTURE", - "9KbgSh6XZaHiDBQE26tGTDuJuUZmVyvJGGabrE7fpump": "CUM", - "5PsnNwPmMtsGZgG6ZqMoDJJi28BR5xpAotXHHiQhpump": "CUPSEY", - "9qU3LmwKJKT2DJeGPihyTP2jc6pC7ij3hPFeyJVzuksN": "CURES", - "8TbsZ3yH1mBHytVpmMn4qED2UeF3FgWUVt1pd5RBpump": "CUR", - "4gWDfyqELKDoVv1xv3NczHEpMgWDg5kDXZMHmvsypump": "CURLY", - "GtfNTYbt41BzVvNLj9ijeYsWsAAVEGidoDoERwg8bonk": "C:\\MEMES", - "jGFnKSMVw5EuGiTZj3nr9Xqrt5tBQ7PcRZVkv5Apump": "CWYPTO", - "FXJAdx38aXJdQd3ABAVu7fQ7Bjh9oMN92eTxszFNpump": "CDOGE", - "4xieJTWvYfWGTdZYjxZNePjqGu74NexmcjLUpfFLVAPx": "CYB", - "8X7emJy8CV5pK7UjyBKCywdfc4MTKShpUddqrqyepump": "CYBORGISM", - "HJ2n2a3YK1LTBCRbS932cTtmXw4puhgG8Jb2WcpEpump": "CYCLE", - "6QztfmSoxygymjzKwGHiWBcVEUxJEkXVQedKycYyyQHS": "CYLA", - "B1bY1U7KYLhj9iSEBCjpAMQB77taEndFSwwBbqRrpump": "CYPHER", - "CexCXDGoZUWqkSnaovysnHXkF6teX4p8tjhns6rhaCZ2": "42", - "6Tph3SxbAW12BSJdCevVV9Zujh97X69d5MJ4XjwKmray": "CYAI", - "GyuP7chtXSRB6erApifBxFvuTtz94x3zQo3JdWofBTgy": "D2X", - "9DNYFn5fMTkFq9Tr92ByvF9vEC49X2cB4Z1PabDnfEYi": "DABOO", - "FabjHjc1druUQoHVtudpNiCpnf73rtLzMkRM1b5NSbb6": "D/ACC", - "8TzeV2nUFfinLkCD6W71DcNRnbTow61DB2nADpCboop": "DACHU", - "APfYrsmioST7R2pdBLfTWi9f1gmgrtDAvJJnUnMWpump": "DADDYCHILL", - "4Cnk9EPnW5ixfLZatCPJjDB1PUtcRpVVgTQukm9epump": "DADDY", - "F4ZL8sHPXAoUaAQ6DVY4Si6uhHu9T5dvciNozdPZdoge": "DAFT", - "HsNx7RirehVMy54xnFtcgCBPDMrwNnJKykageqdWpump": "DAIGE", - "GatSD8nUEA7AZLb8xB9ZCjL8HWC7agVbBYQZv3pTpump": "DGC", - "7mwikw88GRsAHbFpbpyuM3QMq1hXH87d4rYNuM8Earay": "DAKU", - "7MDApufGNAKDDTcM3hrUpUfZ8jcaXdaoSZy1sQdRpump": "DALE", - "98aprruoAHdVvoeRQJmcARd2HhJC4QN32AL9c2bnpump": "DAM", - "G4HrTxyKzLWuyVNJYAKUD3nzm9NW7ADmMUNz4MV3pump": "$DAMMIT", - "Xseo8tgCZfkHxWS9xbFYeKFyMSbWEvZGFV1Gh53GtCV": "DHRX", - "8QwqzHgsGcVrxgT8Bf4U3MMsiAsFUpxTEEJu3RF7Mpvr": "TRIANGLE", - "6wUUYjt3gFT6QFQTTsGtP8GGN4VfmZmAjfsbdaCmGX9P": "DANK", - "GPwwihLa1w9Qsz27SmNrj7aLVnE7pr2cAvHe6aVVpump": "DANK", - "7xUV6YR3rZMfExPqZiovQSUxpnHxr2KJJqFg1bFrpump": "DGPU", - "85aM5XJhdDeUw4MbGKM56zmWnsRyh76zUVut97uPjiCg": "DAO", - "GEJpt3Wjmr628FqXxTgxMce1pLntcPV4uFi8ksxMyPQh": "DAOSOL", - "nQ1qgSpXWi71twnWPFjyfCtcbUXbVyQb64RfHKwRpKE": "DAOT", - "9hnmRRR4jdGvDM89WMyq9UdDfWT6iuDYAjFZQfn7pump": "DOGGO", - "6pvtv14ye2nvxqCR8fc8y7TTU6SkLGyaSVnVNS9V6777": "DARK", - "HsZvkLneVfn3HVEdg649osbjicUEpVFdNZgYH9Mxpump": "DARKANI", - "Dd2T3apN8Vc1GRr8oPkCpnkNu3ykvKDcFt1AEk8ypump": "SHADOW", - "Ezn7q7Mrp8Asij8SQrT6BiuCU9RhRcEv4S8jDfiBpump": "DARKDROP", - "8BtoThi2ZoXnF7QQK1Wjmh2JuBw9FjVvhnGMVZ2vpump": "DARK", - "5YxXvio26Mv2RfZEYMPVcStKRVMG7TTjcWJjYjjGpump": "DARKHORSE", - "7D7BRcBYepfi77vxySapmeqRNN1wsBBxnFPJGbH5pump": "DMAGA", - "GjjDzSuRzbzf8bQbcZR1RgEpPmmHKyTMqksQJhx5pump": "DMG", - "J13cJvKEhXDSBW7T7Q61xVgNVRThRviDmdmJM1X1pump": "DUSD", - "4fwCUiZ8qaddK3WFLXazXRtpYpHc39iYLnEfF7KjmoEy": "DPINO", - "FmQ7v2QUqXVVtAXkngBh3Mwx7s3mKT55nQ5Z673dURYS": "DARK", - "23ENcgMStoFMYYj5qdauaca3v1ouvRdZXTdi55J1pump": "BINARY", - "F1QmooYUjR3x7MktgeNWVfg4ELbK429baMQDcYZTVEiL": "VEIL", - "GUiVKWZvtNnc273wBRLEwMBxCoPffPvhRC2ghPySwAFZ": "DARWIN", - "FQ1tyso61AH1tzodyJfSwmzsD3GToybbRNoZxUBz21p8": "VVAIFU", - "7GM8291Ze6AA9sA4Lr2B5nhLWWZJ6zhks9p6j8CMpump": "DASHPAY", - "Duxeg8HrG89Dq95oyiydrnFd8irZhjApGZu8PYrEpump": "DATACLAW", - "CwsQd329uL1ccxT3my2EJCEsciT1CUfk2BUmPF8Ppump": "DATAWITCH", - "9GM8icX8MsyAJewmqQp8RQJqUfeJtTrrKr4gCRBBpump": "DATBOI", - "5jFJCvNgg8ytGWBygoquaUC6bMZyr7C5jmGHECBzrxR5": "DVINCI", - "DZSs9nHSr9BBunLNWd6PDstesJ4PBLMFVK1GbZ9urYNZ": "DAWAE", - "7cb67ev3jvBKaAH1tnVM8FL8WfQ96sa2TYByEXajqx8N": "DAW", - "CAJ4iqYG9dMueHaQyfBGkb5SJUw2awkMvKTansAupump": "DAWN", - "GMzuntWYJLpNuCizrSR7ZXggiMdDzTNiEmSNHHunpump": "DREAMS", - "33qfNgPi1DC3Ja2oAQS9dpvuVb1jFLheEmJpvA1Vpump": "DBS", - "DeaVrje4tWsFNJ9Shs1CtjfXDbQsNeR1eSWtGxjJoFpS": "DEALR", - "Ds52CDgqdWbTWsua1hgT3AuSSy4FNx2Ezge1br3jQ14a": "ISLAND", - "BgwQjVNMWvt2d8CN51CsbniwRWyZ9H9HfHkEsvikeVuZ": "DEP", - "3F57ME7sgqaGnajTd5XZeAzsgmE1nxyZvkAUsvRgpump": "ALGO", - "DBRiDgJAMsM95moTzJs7M9LnkGErpbv9v6CUR1DXnUu5": "DBR", - "9NQc7BnhfLbNwVFXrVsymEdqEFRuv5e1k7CuQW82pump": "$DEBT", - "vLieQF5eDqUuuk8RzRaqgAHkXr7bSEBZaWk9Zfibonk": "DEBT", - "DEBUTr2WcEsjkwKhqbRLqnuFKstX1MrEuvaz5xcoQTgn": "DEBUT", - "7dgHoN8wBZCc5wbnQ2C47TDnBMAxG4Q5L3KjP67z8kNi": "MANA", - "BiDB55p4G3n1fGhwKFpxsokBMqgctL4qnZpDH1bVQxMD": "DIO", - "338uDSAxxn1ChHq6ZRUAxJCgpLtCBZV58KUT2bbJF4cD": "DEDE", - "AzC1u8NPdpS6DVBs8Mv3P6egZ2AhN9KaGaY64xVMpump": "DEEBO", - "E7ErFx5dRoAxnDphWRmE8DjfJBr2fvjvnX3cgaj6pump": "DEEP", - "hTRDn7zE5tDHRnjj6Qms2WG1zEGv9ii6AiwfgbFpump": "DEEPAI", - "3qVpCnqdaJtARzE2dYuCy5pm8X2NgF5hx9q9GosPpump": "DPCORE", - "BpRMSjbQqm26f51HYKeBLfKEs43yXUhaKA8DXvCxpump": "FRIED", - "4YTyNbYrZiKYkweEPAQySXV6NbvVe34qHemDRpxRiEve": "DFV", - "93VXFhKsGXsraivwdwAwoVXHKd4U1TFL64oQYpVjpump": "DNET", - "DwDtUqBZJtbRpdjsFw3N7YKB5epocSru25BGcVhfcYtg": "WORM", - "CqQyAZjB9LGFTG95eiadGTkfhd9QA12ProeKsQmondo": "DEON", - "68zA59nP4Luops7SMAJgWFoWraNmak2c8r2FEJrHpump": "DEERMAN", - "6pKHwNCpzgZuC9o5FzvCZkYSUGfQddhUYtMyDbEVpump": "BABY", - "DEEZgP19ZPovNeWRJZw7KuNbkLH6xjNjZ4HsUJnmZv7J": "NUTS", - "HXwh9i8D1LKkVK8WfMjFPyiyVZ4Auh9aLD5ut4LCRqqR": "DEFI", - "FfroP8rQZmaJtZ6sATd9yKvmFWb4dB1DqHWTbBtesoar": "DFTV", - "DPTP4fUfWuwVTgCmttWBu6Sy5B9TeCTBjc2YKgpDpump": "DEFIANT", - "AAASV2sCaykNGdEQzJ1mMVNfJMsgEtR7PJaqrCuk4bq6": "DEJAAA", - "DFL1zNkaGPWm1BqAVqRjCZvHmwTFrEaJtbzJWgseoNJh": "DFL", - "GoLDYyyiVeXnVf9qgoK712N5esm1cCbHEK9aNJFx47Sx": "GOLDY", - "TUNAfXDZEdQizTMTh3uEvNvYqJmqFHZbEJt8joP4cyx": "TUNA", - "B8RHrVBxSjBGKqAbn1tXo6CWjvt5jFkkqjbCZtuDpump": "DUSD", - "CPG7gjcjcdZGHE5EJ6LoAL4xqZtNFeWEXXmtkYjAoVaF": "$DEGE", - "Fd8TNp5GhhTk6Uq6utMvK13vfQdLN1yUUHCnapWvpump": "DGN", - "A7n89LqW67HJKzJkdWZa2xojuK4N5GBKHz3dfjATCZPz": "DEGEN", - "5NHd3MsP6dXi9r1saPkeB2DoZyXLvPiqv4n9J54Cpump": "DANNY", - "6ztpBm31cmBNPwa396ocmDfaWyKKY95Bu8T664QfCe7f": "DEGEN", - "AK9yVoXKK1Cjww7HDyjYNyW5FujD3FJ2xbjMUStspump": "$SITCOM", - "oraiJP7H3LAt57DkFXNLDbLdBFNRRPvS8jg2j5AZkd9": "GNRT", - "EKSKQP7iz5s9L8db6s9T2u9o7cczTCWEUscsXHxatime": "DEGENPING", - "FdpEvMwXM99WsCr5zwrQ5BqgQTSLULpewpXjfs7ABAGS": "DEGEN", - "Gu3LDkn7Vx3bmCzLafYNKcDxv2mH7YN44NJZFXnypump": "DEGENAI", - "8AGrudQDbjNjnHzBsrndfVDBHgg6KBJ7RN6j3hbfq3Qh": "DEGEN", - "3UzkMymHpqDijXYaGsz8Vbc1Vk4tKBED5TDT9v6Hbonk": "DEGEN", - "4Nc2EhF8vqXcjHpbUevYZv9Bv4aKb4fWi7cju7c2pump": "DEGEN", - "degod39zqQWzpG6h4b7SJLLTCFE6FeZnZD8BwHBFxaN": "DEGOD", - "BU4eP1vCR99amXKsMXhctX8YpqUa7wbULQ26XaQbazkS": "DEGO", - "CkADK7xbwyyU4TUs2ExUbJcASUqawVB7ZKSr1pexbonk": "DEL", - "GBJpKgvZuEc3fzHZKoedJ14R8N5JwMd8MavfomNcpump": "BULLS", - "BDWvvvKUS9c3C4Rz1w9tk5uD519CzD2f6n7zBk2Bbonk": "DELULU", - "6KmwvVo8gDUVAnoAftMuyUTojfx6xtgm2xkHp2ixpump": "DELUSIONAL", - "8bUbe1ujsM1G3JEbBWVVCXa2widmuPdKUB2rGKMYFw7R": "AGI", - "9WiKyhfSX3wCPw1Xp6it94mA71XmBdSdxirX2TNqpump": "DMCH", - "12J2LD3tuLfdiVKnWZMHRMrbnXDY9rM4yqVLUa5yondo": "DNNON", - "313t1ZZcLBEmw4uWRjSei1xCDEK4B73KqawJ5xfCpump": "D.O.G.C", - "9TY6DUg1VSssYH5tFE95qoq5hnAGFak4w3cn72sJNCoV": "DOGE", - "KQijDbNJ6rPCqhtXrfH6gKa5cH3a39At8vHq34nnPbF": "DOGE", - "B54Fyr1XrWNA5RsRRkovoVFwQwzwBfqaJPLoFR4pMoAX": "DOGE", - "Dxmi5HRzJS5ov4siafxX7Ur7VVJQT64SPFcK3hsaSqtU": "DPNG", - "DEpHYkW6nEm3Sxhb2rwftatrSAV3AP3ac6MgAWBbnsS8": "PHY", - "J2LWsSXx4r3pYbJ1fwuX5Nqo7PPxjcGPpUb2zHNadWKa": "DPLN", - "HRiLLm6hYiSdniFGjvcSvQ7XJrA8YSoktcqFbWjmpump": "DAKU", - "4DBgRWs63QzBwj71Qxiiz49DUZJi2QgUha7cbadepump": "DERP", - "14bLBcB1iHUBGioLSwjbWBsP26nZKU16Qg44p5Vderp": "DERPY", - "8MhHH7nBxmukybUx3xqovgLDTetVzj3sogBXPueTpump": "DEVAI", - "B8YVF2CA2hv2LnPdWpjNmqcS7g3tqhYkYm3wugcvHELP": "DEVELOPER", - "7gbEP2TAy5wM3TmMp5utCrRvdJ3FFqYjgN5KDpXiWPmo": "DEVIN", - "CeoReCwAmt8iqjQoKQGEMqKERDWGxxDUED9zVYg3pump": "DEVIOUS", - "9nD29a6TMDFHqAXS3iJ6nwLByLBS1gx2664ieQf7Zo7H": "FUELX", - "DEVgebuRAfMgnNyZhdaw5w9DMhG654FcLzAfjFx9HsBg": "DEVSCAN", - "DVr62PSjVCrEwmWVRdKQABqJumb1jmJGKa8BkUG6pump": "TOMMYJINGL", - "2jZChcoRaYfWVXjEefJRDcpoRGtSdNZw8i2TtkhSpump": "DEXAI", - "GsNzxJfFn6zQdJGeYsupJWzUAm57Ba7335mfhWvFiE9Z": "DXL", - "XLnpFRQ3rSWupCRjuQfx74mgVoT3ezVJKE1CogRZxhH": "XLAB", - "GfPGfHnNbbKBvMGcupzixvgSvQAybBEe6DtdQ7sC1UWC": "DEX", - "EfPoo4wWgxKVToit7yX5VtXXBrhao4G8L7vrbKy6pump": "DEXTER", - "FkqvTmDNgxgcdS7fPbZoQhPVuaYJPwSsP8mm4p7oNgf6": "DTR", - "zpfeF4zXpyXKSd6Hc6r7yRqWKZ289Bd3JVJW4SWKoLV": "DTR", - "DTFc41ZXq8Mr84pLD6J68YgyhxNmvJ9qnvZQKCotPZPg": "DTF6900", - "sctmB7GPi5L2Q5G9tUSzXvhZ4YiDMEGcRov9KfArQpx": "DFDVSOL", - "Xs2yquAgsHByNzx68WJC55WHjHBvG9JsMB7CWjTLyPy": "DFDVX", - "2kBzHjLgm9rwrbZikLk1dkx1Bt56Spc4cjdYH8Hh89Em": "DHANDS", - "5wJPNcDnxD2W24A7Xo5KdPsZo3aGGhV6LeTorbs8pump": "HODL", - "BmKNgdoCFAhjEszjfhrPmt8Kdq3TdWbPtdQoSKUdogHz": "DMTC", - "4c1HzBShCHc1EckQTVXZZ5feDCgkDweaw3t3JQQqpump": "DICAPRIO", - "Dice3uJ6AY63bKRHk5Mn24qdNLhBZJFJbaeDsTQrzBPE": "$DICE", - "8EHC2gfTLDb2eGQfjm17mVNLWPGRc9YVD75bepZ2nZJa": "$DICKI", - "8E5pw1g1nQpmgVyeS1vZx8LokAZvuZgL6G1Uhr4iVbAB": "DIDID", - "7jC1eiCo7pnrj2fL4k3RvWGTjrSSLiJBxYMm4fiWCASK": "DCASK", - "AUdUEc98MGfEHJiJfCgMaW8gKdcfNDio8BFzGKBwjztC": "DIGI", - "EwFTbdtH1FZQ2ZtDTgtYfzU2fBsxuGQC3nRHAFHpump": "DAT", - "6Xm7YzZt6JMhGofc6W41ybByfpsnHD5er5bdkK8tpump": "COLLECTIBLE", - "EhwhYhMJp434kqQeFRRBCwCVPkgdjrYFcBXGozxVpump": "GOLD", - "2RF38YBj5xMSzap94CrsQKDTZ5FTd8fwrdQSUzfrpump": "METALS", - "5LS3ips7jWxfuVHzoMzKzp3cCwjH9zmrtYXmYBVGpump": "OIL", - "EcEP6shgosHfg4m5b7wWTHHJZFWzoF1iqcdZMBzwpump": "OPTIMUS", - "2uaTpSujZBYwBZNXusmW7PqM8Vi4qwbyotnfWhaN9oT9": "SLOP", - "Hme1fwfqVYmfUaACE5AtkfqfRh34r3JyT5GYqyaTpump": "DIH", - "61VtkXr9mRXtrFYe2T9arWYX52sbJnkAUQgfjGuCzox2": "DILLY", - "14jazxRjHyaYsGoFk1u3CZSmgswYginbtTK8Z9PAboop": "DINGO", - "6VYF5jXq6rfq4QRgGMG6co7b1Ev1Lj7KSbHBxfQ9e1L3": "DINGO", - "6Y7LbYB3tfGBG6CSkyssoxdtHb77AEMTRVXe8JUJRwZ7": "DINO", - "FdqkvhQNzrj2iEWBTnKvLaegWWjCxNgVkXC5bJ7guPKF": "DINO", - "33eWALS9GkzSMS3EsKSdYCsrUiMdQDgX2QzGx4vA9wE8": "DINO", - "6wCYEZEBFQC7CHndo7p7KejyM4oGgi5E1Ya1e9eQpump": "DINO", - "BkuhUp224bBip4BS5mLMLG781dxDzFJGc3QPQfDDpump": "DISCIPLINE", - "FbmmdcCYHL7WETG89xtWmNFMzQAaQ8Zs9NXVbimibonk": "DONT", - "mJf1xT3suXtkXBCfZcE9oUUuyxkvSgqYBWiX7v1ondo": "DISON", - "EPuZ1X6pPzac3ELPsT59LStmgaSr4kBJvaAbL15Fpump": "DISTORTED", - "Ddm4DTxNZxABUYm2A87TFLY6GDG2ktM2eJhGZS3EbzHM": "DISTRIBUTE", - "2AEU9yWk3dEGnVwRaKv4div5TarC4dn7axFLyz6zG4Pf": "DIS", - "E1kvzJNxShvvWTrudokpzuc789vRiDXfXG3duCuY6ooE": "DITH", - "7GB6po6UVqRq8wcTM3sXdM3URoDntcBhSBVhWwVTBAGS": "DIVIDENDS", - "GcWVDkoaBWZYW7bBgqCHDiqAmfnwwPxvbusA3s77pump": "DP", - "6uRY9e8gMaogSQiQmzAWJbbEvVo2zyZMyypc9AGXpump": "DIVINER", - "DVYcTNFVGxePLgK8rUjViJvurRmTnD1FZUBR7puADymT": "DVY", - "A5gVj5j16U4vCTMXQYYrhkmWgx6gej4nAeZRNQMLUAfy": "DJCAT", - "DLMmRN9rbZspAAC3a1HgDHMC893Y2Ca9GjoNh9StwnYG": "DLMM", - "DiSetnR7k57wmfvywJhUVjPwWfg54SdQKxQdJEBYW23B": "DNA", - "AvuNqYe1Rj6eEzan78NYJ1nD3Ldt6CT8wpwGNrE2pump": "DND", - "34EBTRNxjqZo3CgpQD3aaW3CC69DkP6BQUEx1Dd2pump": "DND10", - "GzQzkt2B4Jr6whWVBF7XqkzWvoUy1jEd5z9tczzGg1rH": "DOBI", - "5vb8AXytbQenTds4xKinp1FEoWAK7xoiNZw6hCh4pump": "DOCTOR", - "2thfFEH6nR7Qbqd315Dt9BjSJRau9YQ4mmBhkAuepump": "DOCUSOL", - "DoDoYogRFcc7XRwdJfMQKLvccYJKAvqPnqVpFZq6kjYs": "DODO", - "GW7MWKG1ucAr6KDAUG7aeEujTGi9MEtdfqEZuGhmTaks": "DOEG", - "Dogg6xWSgkF8KbsHkTWD3Et4J9a8VBLZjrASURXGiLe1": "DOGAI", - "BNsEiRYcktWVft8V1sgK9Achk1cLQ4fdZLAfHzWqbonk": "DOGCOIN", - "SsKFgDPEqyzAM8nWPiiX7MGY7iNDTEX6DxRdxmkpump": "@DOGE", - "DpBzjtgGLF7QA9Ug3eUVGbnqa6j3jvYBn1XuQuktvfhm": "DOGE-1", - "4xB2NGznSwQXAAjRsyAputhonM2uDD1FYwnGo92pdoge": "BAKA", - "9UYAYvVS2cZ3BndbsoG1ScJbjfwyEPGxjE79hh5ipump": "$DOGEAI", - "CkSFj9MNEfWffXPuJxfcWRWdqHYUMAcvuUdJ7muSLCKJ": "DOGB", - "ChHAfQsUznqUpvFSNQsu42KsRr1t3ct56fBigVSHpump": "DOGECAST", - "B4cYZYVYeHgLc3W1pCduCYkoS75G6roPaPdPoBCFweNJ": "COLANA", - "EcYK2XNG4wWr2vDg2M2Hrts6SrU2QB4NzXLBf888pump": "DOGEFATHER", - "A4RfnhcD1hk2QiVUx3TTtR7Af2RkgjvyJMcnogj9bonk": "DORK", - "5bXVGNnGhCFAK9KPh1S9whFkhVxZy1mREWFbqB499xUP": "DOGE", - "DCHLn5uLCDjPcmyxqeV3EFA1hAT518RR3u7gQe8iUiYQ": "DHC", - "BjotV424H4UBvrAiGFGjQGztLxoafxM4HSdCXZR6pump": "DJI", - "E6UU5M1z4CvSAAF99d9wRoXsasWMEXsvHrz3JQRXtm2X": "DGLN", - "7ZCm8WBN9aLa3o47SoYctU6iLdj7wkGG5SV2hE5CgtD5": "ELON", - "7Mxvi7bi5bnjELB4QNzQ4VrQEPeBgPbWwbfwE1ydBAGS": "SHIBU", - "DLScRnWofxiYGqnvZWGy9Gt98MPqKdznaK4TRukxpump": "🐕", - "8LFpoJWGS65GMDL7jPKPPgvns3g6vEDg15BmcUSsezuR": "DOGENARII", - "Hk82ay38uGpYgqjdXXCiATavVQwAMaBNyxsUWbapmKuL": "$DOGE", - "BvSyXBvy76mUgzLSbvvT4NQw5rSM4P5zAsdnvqUJpump": "SDOGE", - "7xZCdhj7rGhnKndRpU3NHgZhbFMkPLP2ErDr1AS8Chsx": "DOGEVERSE", - "9t77JBusmE5yEad6idpT1rWf7NQRPoHvV6DFaS7dsWy2": "DOGEX", - "7x1xuoKGKxQuFcw2KjGPFtTLET6GC6LAygTRtPvYbjug": "ZILLA", - "HVpsW3uvqf5vq1onTQZ4VMRmkY5sioHyPSEidyj4pump": "DOGGO", - "2At7LMSFupTLnYb32w3rHGahhpqN7oc8jGQjhzoHbonk": "DOGGO", - "dog1viwbb2vWDpER5FrJ4YFG6gq6XuyFohUe9TXN65u": "DOG", - "4EyZeBHzExbXJTM6uVDiXyGVZVnf9Vi5rdBaBCFvBAGS": "DOGHOUSE", - "2C9ynB9HCqwZLdKjg3TDppTwYdShwwUdwtmymFe6pump": "DIB", - "EWYtkRuavbDKTsHqfSUSyo5diffxduGcsMufjpWfpump": "DIB", - "7EoxBarbcbdmibuTZ4MgWzuBNQkXqKuonjfD7Eyepump": "DIH", - "3rnDTgmTXaYuPdWZdzewJHrLQ8mcvqiLkFMDpE4Mpump": "DIH", - "E3HaoVGnezn7M8D8zvBnim2zj45otSMtk1drakNMpump": "DIP", - "3G8js2FjDtMbVDzh5ACaTLGHGDwBhaM5LBNuHKMeZ7Bd": "FIRE", - "F6Vyxf5c1aMEQDqTHiCzY367wwwtHuMCk7W64ekopump": "PUP", - "3XxvmED354933DwSPJuzB7SE9uiWpD1ErydDuhmbFRMk": "DIP", - "55KrNjWHrkgxCzTLvcgaKYDxaopuxZK8vETi1CJNpump": "INVEST", - "8bk3hKqrq1NgLKck937Fj6PoY9jAJgGpUKQ28qutgqha": "DOGL", - "7LsFasVybz9nn6kRrHPA2w62K7UaWfg2SAUQAaG6pump": "DOGMOM", - "53ctv3wwFXQbXruKWsbQcCe7sefowyu96pXK6FRLTjfv": "WISDM", - "7ZRgHFqwdzvqhfmg19rSQBD6CZe5iEDkuZoaNUhpump": "MONKEY", - "79dGFnR8XUusyDiK3n8yZ6FXhJjWLFgzdsi2SkUpump": "LIA", - "99XLnpFF9UAJcB8bY1a9mmnVebSgLiyPiPjx8GY2pump": "DOGSHEET", - "8AKBy6SkaerTMWZAad47yQxZnvrEk59DvhcHLHUsbonk": "DOGSHIT", - "BXebtR4k2WiaZ1HJmxcZkoCdxSBx1g1xnEpVra9Ppump": "DOGSHIT2", - "2FKMWYwr17j7AHexcgcQbn5gwZTsYR86qBPrU1BHpump": "DOE", - "FhhXCnCbxBQ4nX4a4PBTuYG3gbETWixoNejQkPKJbonk": "DSTOCK", - "CjqxraDuTMEcfhdqY8qEaMY43icdBrkt3EXciNVpump": "LEMON", - "6UfynQAUASwqxSSEENzL7vMR1wmpQS6LXrNn8f3xpump": "DOGUE", - "EKpQGSJtjMFqKZ9KQanSqYXRcF8fBopzLHYxdM65zcjm": "WIF", - "5BKTP1cWao5dhr8tkKcfPW9mWkKtuheMEAU6nih2jSX": "NOHAT", - "G2FVt4LrYgZ9Grmy4Pb7FA7SKdLRZ38mAxTiLjex5Wsa": "NINJA", - "2ioyweEeV4xJCkFJvh868X9iP3L6Q31MVCawfbJLRTHq": "WIFOUT", - "jD8gVEjhkJos8caTEuVApbBcbZfCeRovxMCGwe6rtNM": "PANTS", - "ELdqGtj2MhejZzudGfz7b798si2Av5Asuw9ryEQSpump": "$SCARF", - "H33XL6HHDReCVRgSApZpsXM7Hy7JGyLztRJaGxjapump": "APPLE", - "DfAwMXCmAyxt19Q1QxjHyS2ggskVwCnFaYSqgTF9pump": "SHDZ", - "HLJzUzWU7eFKDCvuoJ6cQCYxkinE1cXCZjxWJWwWpump": "VINE", - "3vmfEaTR9M2Pp5JcFNC8c8u6U4eFUBdq6FQjgPpcnfKS": "DOAI", - "4YK1njyeCkBuXG6phNtidJWKCbBhB659iwGkUJx98P5Z": "DOLAN", - "5AnPDx9GposBi9jSW2dFfE5QQD3FmXbudoquMNDxpump": "DOLLAR", - "6YkkKzsCLEQoUMnWMumvn7eYeUPRhXBcjPDJwwbLpump": "DC", - "AXQyzYRdk6ebp3VdwSNNNFR5Ssm5rMVTP84EEg98pump": "DCA", - "9fWeqjzb7xG93T9FPaWggTecvyZZDTXc7VgzNWkisqm4": "DMOON", - "79yTpy8uwmAkrdgZdq6ZSBTvxKsgPrNqTLvYQBh1pump": "BULLY", - "ZFwVfRtyQw4ark6ci6SNdvgMvTk4iDmb8RZU1SqBAGS": "DOLPHIN", - "FaUKsgcuqAd9sCaFz3if7ia1DasaxVoNPTXWFs2GScWN": "DC", - "FU1q8vJpZNUrmqsciSjp8bAKKidGsLmouB8CBdf8TKQv": "TREMP", - "xABfKiG2KCHi6keTeLycW1iK7B52wJmchSWXu3YrsDp": "DONK", - "Be63Mpktk95oaSnYaWN5siPBoZDohpNGxc4WNLGupump": "DONKEE", - "5Rs53fY3q4t4mLk9zBQ45cVNgF1RH7NqRNXiP6Pa5rYH": "DOKY", - "5Psvm4XSKDzFyXykWeYxWUPFgJDfT4EWQMa6mabCbonk": "DONK", - "B4G5shxukCDRMLsZue6NDAVMbSzViPn9SFieZG1Ucook": "DBC", - "BUAv6q4v5E2Pt5ms15uzxhFF1L6rXsG5i4NmSim9pump": "DBTC", - "87sBZeQCE3A2dkUNs1tPoYHryiiyt4BaBLqorzSQpump": "DONTDIE", - "RrUiMy3j9bzhgBJpXCqpF33vfrGD5Y9qAfbBVbRMkQv": "BITCOIN", - "bDz6NVehx5mr8E1e5d2c8ZAqjDe7DQWZeEKPpyvdogs": "DOGS", - "3iCJnnDYFT4RrPs7vMzw21sfVfy4br1xWmJioz7FGJUR": "DOODI", - "DvjbEsdca43oQcw2h3HW1CT7N3x5vRcr3QrvTUHnXvgV": "DOOD", - "JDwzFSxcUvLubUb9xAuuZNvh4bbcEJcuM9TezpmRHVWF": "DOODOO", - "F6TsRcjtLBzkdtZYqjTPVq9WtnwHMMc1WcQguEgCpump": "DOOGLE", - "8Fvp3K1KxbDALnyVdy8Pvy8Xw96XVWjpauXLi7pXmygj": "DOOM", - "7RhcnEJ4Nz5BNkUg5V5vWZaGGiFgnTjEPvLFYLGfbonk": "DOOM", - "83P1gCFBZfGRCwJuBt9juxJKEsZwejJoG66eTZ6ondo": "DASHON", - "8Kqmghm1mkXbKYqt2wGnHxwnRdEjD9nayNKD3EanWkcD": "DOPE", - "3krWsXrweUbpsDJ9NKiwzNJSxLQKdPJNGzeEU5MZKkrb": "DLORD", - "8uwcmeA46XfLUc4MJ1WFQeV81rDTHTVer1B5Rc6M4iyn": "DORKL", - "7voKerVHPvbXde3bEpyNcdodVEufq17tW6eZLUUkpump": "Y2K", - "3KDgfZkd3jZ8S2ZwynouuXAjUXGTgKcp1SM5kZ29z76Y": "MOOV", - "6m19zvNWYbXS9Y1LhFc1LFXqecE2eErRrR5uNrgbV6Y1": "DOTZ", - "J6pQQ3FAcJQeWPPGppWRb4nM8jU3wLyYbRrLh7feMfvd": "2Z", - "Gekfj7SL2fVpTDxJZmeC46cTYxinjB6gkAnb6EGT6mnn": "DZSOL", - "AQiE2ghyFbBsbsfHiTEbKWcCLTDgyGzceKEPWftZpump": "DOUG", - "Dsad47jgGNoJ8p4LJQ8x4yCFRMHBL7kd1Js3TJj7X52C": "$DOH", - "DQnkBM4eYYMnVE8Qy2K3BB7uts1fh2EwBVktEz6jpump": "DJI6930", - "6UzcYYWb2myAvN86uQ9XAKtm7yQkddacGxktT5RkGood": "DOWNBAD", - "8h3jsXbvBRPKGGRPhoYRHr7ca8qH3tBeaE2S93X1pump": "DRA", - "GiBrdw1tF8nuJxWuhTp83ULEMY9uJkYUHQUBzwfEnw5R": "DRAGON", - "GoCr92MAhiE1kxXyGx7yPdS9qmVxhcJaKN7aNZumgcz6": "DIAMOND", - "CwcmxzV8KrhJXdzyHs43shCuqiVTMQKZ5MnwmRwrpkin": "BLADE", - "5uEduS1nkyE2QW1oEZei2pnCN3oym4ziaZ143A7Jbonk": "DREAM", - "B9zze29or7CJs1nBrigSCWSibfxvkmieFXcGg5cxbonk": "DREAM", - "HQPWKLGfLDysR3y18jEKTfRUepGJcYM2xCEYhr4ffcVk": "DREDIT", - "72xMD9BZEEtT7o2C8tXaeYA5QkV53WJ5W83kZKubuCap": "DRIFTY", - "DriFtupJYLTosbwoN8koMbEYSx54aFAVLddWsbksjwg7": "DRIFT", - "Dso1bDeDjCQxTrWHqUUi63oBvV7Mdm6WaobLbQ7gnPQ": "DSOL", - "AZjYk1DqGnLWZzGyyNzYTC5xqUyNRHLhiHXhZdGKUhiu": "DROID", - "BrYANThKaAbjZZH5XWLrw26NzMbfUNmBwbZiMe4Fj5Mk": "BRYAN", - "MLoYxeB1Xm4BZyuWLaM3K69LvMSm4TSPXWedF9Epump": "DRU", - "457PbiajA8Tsh7FqS2Y68ncdXcGobkeLT7GP4mYQpump": "DCC", - "7woCERDM7WV4XaBLfdGwvHBX3EJHQqp8XjArrgSFKkpL": "DUBCAT", - "EizEFyqCs2H9134697crr61cfKbepew4xWHU9HPaTUpq": "DUBS", - "DUCK4JNpgd6SziPDLKP7fogV5KQsAg6Hdx5MCwwYAYuF": "DUCKAI", - "BavuJ8bntC79A8aHTxQi1EUhcCnqvEU8KSBE4sVCAaHc": "DOUG", - "97TgtAnya3WC6fDRhzeeV7B5ngLnemP6YFjxnxYxY8ng": "DUCKY", - "DE8NNksD5m3Kd4ohJAzTwJHLss9zbXdNUegEoL4QxD4C": "DUCKY", - "HXjiARvkzib4NabiMGS6GPsb5xTGyDpuympVHzP3pump": "DUDU", - "GP4kcMkX3j3aDSAjoYUPqSSBRYS91cmL6ULYhFzpump": "DM", - "25HwajZbusQvso1XWA1T41Cd5LVVxHsv91P3Ahq3pcTK": "DUGE", - "HLptm5e6rTgh4EKgDpYFrnRHbjpkMyVdEeREEa2G7rf9": "DUKO", - "A2khRbhRJNrAEHj95htivC4cR4VbJwfssDH5FPPbP4m9": "DUK", - "556BVtBSN6kWVXm5q9KW3FLXoFjmwcMi8C4ztKrH4Xk4": "GME", - "BMhHXK6XQSGLkX3KjoUcppmBsssUKAqUYwQ68tMzpump": "DM", - "9re9eKMd39Aaanm8wgFKmLXNVX3ukS5gK4J5aCJepump": "DUNA", - "Xs1CqRVt2mGVfMSHf7M6cDhkjpw8HDF14XbQNzDnGg4": "DUOLX", - "fRfKGCriduzDwSudCwpL7ySCEiboNuryhZDVJtr1a1C": "DUPE", - "2K2EcwAKDjU6Dg17WRwKshhr4Qu29irvRt3GU3uphrKg": "DUPE", - "DUSTawucrTsGU8hcqRdHDCbuYhCPADMLM2VcCb8VnFnQ": "DUST", - "eiu9KULXfswbCoZSQQLpTPnWRkvp1Fq3i8gRnsvpFPW": "DWAIN", - "hqJXutLF6f7DxStrWCrnZDfXzbNTZmvi3KheVi6ondo": "QBTSON", - "4Hx6Bj56eGyw8EJrrheM6LBQAvVYRikYCWsALeTrwyRU": "DYDX", - "DTUW2CFo71KnTNSFYX95jQ8P8aJVQVr8MEF1AGMm5WGm": "DYL", - "2MCmXsjSXHoQYR6ckg6Af4mhQKDMJMGy6JKh8C4Qpump": "DYORHUB", - "BjjvKX5k7gQoGRmvQAA5WMr7EkQ2cirGTSGxAznDpump": "EARL", - "ErVrf7WrMS8Rw4b6ZkYSVR5TvMjKRMjSLH8cwT7jmbgp": "EARLY", - "FradHppu1s67CegxxSARmQmyJ26yCF8soUZaSE9qpump": "EARNFI", - "BpYiU1dBXU1fdB64jbR93wHEw3Y47QeRLZvUyLQondo": "ETNON", - "8cVZCdP973kupdt1TktpD4jq3k7Jpr3FiaBAxN5Kpump": "ETF", - "XsDYSbgLh3T7oh59UmRX1F5WjDgJD3nmzACaG5LR1MB": "EBAYX", - "FKMdhDBjirErUMQbffsZiuvjptSwNnFW7kf6fvpTpump": "ECHO", - "BqPqrrQuoQXFGGEAEMnPmDgZ6RWQCajWnY3V6Yp4DZWP": "ES", - "68dLqMSU3bkooGwH5J244aoXXwTgFohwVnWUeEApump": "ECLIPTIC", - "APBcWeYBwkBPMtyEj1QGy1AFzEqnYcQcVYCQofjwpump": "ECO", - "6XYXRpLfs8J2MQESojWa3hDBqdExAUNnMciTdKzppump": "ECONOMY", - "6wQDzAZT17HYABu7rNXBDUSgNzDeGUUUzY2cS8wpEGAc": "ECOR", - "kH6hPcpdJqeMAATYU7W4rzqZuzYTkYr6QqGYTLkpump": "EDAS", - "2Zvo6bnwJtyXhsPgbcfajTrEtB7NwrJhK8mMmtEXvHHc": "EDSE", - "5g6wVay8NYQ5Tspo4irj9YqSFfQoPDW7apkX352N29PP": "EDEL", - "5sbZ3E6x84GXtWmBG2vX5pCTLuvCPiwn5C2Yrs3eden": "EDEN", - "edge86g9cVz87xcpKpy3J77vbp4wYd9idEV562CCntt": "EDGESOL", - "7a8JSTTMnynE689y1AvxHTywdQVFdpfdE61rPvgzpump": "EAI", - "65C5dduDBr9LcAmqrQTafStAKPj3ksfk4iTTBKKGpump": "EDWARD", - "GPrg1CgbBvAJS2SCuf9gF7NmQYsWudfyfWy5SUzypump": "EDWIN", - "EFFECT1A1R3Dz8Hg4q5SXKjkiPc6KDRUWQ7Czjvy4H7E": "EFFECT", - "GqmEdRD3zGUZdYPeuDeXxCc8Cj1DBmGSYK97TCwSpump": "E/ACC", - "4ynyx6BzY2XGFgjjun9Cruj1bSRo8FLsAqNnPsW6jDsu": "EGG", - "CTEa6FdASurYCQCZkyzkzdwFC4JVRvtpNYrCQQEDePed": "$EGG", - "EXA537HSBVpsFijENbt6Muuy9AADUN8dUmYKD4oKbjJE": "EGG", - "egoS1kE1g4JuExTcvG6bB6cg5LWpACQ469uk6DTMuJq": "EGO", - "5t4EVfkb5QU8NZXmTzcSK2bQsNWUr2HL64rRb8i6wpat": "SPHYNX", - "Fc7tEqyfHPoWQXdiAqx62d7WeuH7Zq1DHwa2ihDpump": "CODE", - "89S9RdgynPq5odSRmcCDAzg26iYuRw4wqUmzMbjUpump": "EKKO", - "3KroURETdWLyFReapbdh6nTWQgS7Y6rorLY8jb1pJXoM": "MOOSK", - "FgWto1nfArQTpg3o74sYkti753caPfHNXHG8CkedDpMg": "DORITO", - "2HmJ717Smn26MRn4PzmbGf29Z5d2nU6Jqre7HyELNsX3": "XEP", - "9mV4WUukVsva5wYcYW4veo34CNDiF44sh3Ji65JNdvh5": "TRUNK", - "F47vvwFYuLioQsqEVAjqdY6Yihc8wVRiUcfHGcBR9XUs": "ELGATO", - "G7BJzg55Afx6Tn6J9CJfA444jMf6QJbVjgQAevNTpump": "GATO", - "eGGxZwNSfuNKRqQLKaz2hc4QkA2mau7skyxPdj7ondo": "LLYON", - "Xsnuv4omNoHozR6EEW5mXkw8Nrny5rB3jVfLqi6gKMH": "LLYX", - "jriiULJQwgqdU48hTv1eKqK4ipCiCWBnP9CZbonpump": "ELXAI", - "5cbq1HriesW4zHpFEk9Gc8UT4ccmfHcBTDCa2XcBduTo": "ELIX", - "5voS9evDjxF589WuEub5i4ti7FWQmZCsAsyD5ucbuRqM": "ELIZA", - "6XNxPVyTAWGFEBauPMeVevq5qpszsagzuZzA5SSJpump": "ELGNT", - "CZQe93LL4f8wuHmTzLABELgZHRwkPZAbHwN9BKerpump": "ELIZABAO", - "EF23Avq2cTPnMVTfHacZ3SG5Z8misHmFA2gbt2rKqiYH": "WHOREN", - "5LGyBHMMPwzMunxhcBMn6ZWAuqoHUQmcFiboTJidFURP": "DEFAI", - "DuMbhu7mvQvqQHGcnikDgb4XegXJRyhUBfdU22uELiZA": "ELIZAOS", - "CZRsbB6BrHsAmGKeoxyfwzCyhttXvhfEukXCWnseBAGS": "AGENTTOWN", - "CF5VehjuZvxZZQFP5tybGra6ts574FAwt6Z7GuX9pump": "ELLA", - "7pL98QbnrMZCe5FEKajSdTTgLn6Thf5CFaozLkayBAGS": "ELLIE", - "ACWJRei2VGU4tru1oxKHnfoJ3iachwpTeXfEPUNg5Tv": "ELON", - "BkYAUVMar1gLwuFLv2n5cmB6HhcNtvd86kU3gqAypump": "ELON4AFD", - "2akXpuyFXAVN5YofZpZMfBp2Vxognpmv9NooBMuHpump": "ELON", - "GKpETcy8wYHPCB228rvNrqdDR23yyn7Yux6B2rdQRYKw": "ELONRWA", - "CWX6t6pGJ1zsnuywnyd2ZMZJ7inB2sWuPdsteoT6pump": "ETF500", - "CCgH8T82ABC7Krg4bR4Jj2yotiDQdzk3yg6GLLUApump": "LOLO", - "5CvySD4GB7mrp3X5UXHWqQeKn96gxo7rPC71nKx8pump": "ELURC", - "3RZqq6kMpvziK7Z3m4EVs6DConpnPEnZeb9PmrtEpump": "ELVIS", - "HmjJLAXm4Q4cWEcFx7sjDtuGujLJeeRAm9MWLhuHpump": "EMEOW", - "8Qrc2pf9p24NyJVG1FagnqJXwKw6h5L5McxnMfJoUxev": "EMMY", - "GMkWVrn6A4anxwBqsN5ofk3SjyabYTtQUKpk8c3iKinG": "$IMPERIUM", - "ExocdWVMKbZBsMo21M6c6SCj7n4k4s7vmUVz3mGvpump": "∅", - "9XWWHExgN4yvyLRskzzfcr5AcBaqQ6UtwcDSVpUBsoar": "$CPT", - "B9iPvm8YybydhvMiKAuJuygEKuzspgxdavhFNzzUpump": "EMULITES", - "3zJ7RxtzPahndBTEn5PGUyo9xBMv6MJP9J4TPqdFpump": "ESIM", - "9So37icWcKmuMGFPPR36a82Q1E5gZJ6HFSz7Eynfpump": "EFR", - "9f52wiW2zrt2497HroMHtZtuj7VzWu4AsEusmW3pump": "$NRG", - "HjsZ73QxNbXoUnyXXffoXn6kHUKQqdy6GXd4J1N4shot": "ENERGY", - "FqKdy3CtWSKKiJa8aaUbSbSmVhBKz3EiM52Et242pump": "ENERGY", - "ENERGYi1ApwBAtGLKaeLZ4hRGMuPu1EG8PQTV4EDqFsW": "ENERGY", - "4XQvdipJBdrb5hUgUrbZPPFmp6BCav41n55dc7KDYW3m": "ENG", - "ADMiFUmFUz3tzLozh7yTy2zWe1soM61aE995TZqLpump": "ENGRAVE", - "CuFFw6irQnSV48HwdM4mKGauSwEWqTEztL14kShLpump": "ENKRION", - "Kruj63Qx9EQX9QzukLCBgx5g9AGW69gPDsSK25FRZAi": "KRAI", - "4Jsv4fnNp4gwtZ6omrDhEKbmFtpmLAt6nqTqt44wpump": "E", - "BncvtBGs4JqgYZwUoq3EN9q9HUFqJKTfWpvCsHCondo": "ENLVON", - "Bp26APthMuM46gMFTo5KYpo7b92GN2xSCor7f9oondo": "ENPHON", - "5s4BYUXLuvs9ZcVDTxkTpKhThWFSpaU8GG55q2iySe2N": "ENRX", - "HYnsveQmKoG3qPhWypcgx938widsvJ9BECd9xzmKpump": "ENTRO", - "ENTxR2RP8NtvhXzMNFCxE1HazzdV9x7SuZqGyAb4jdED": "ENT", - "eZBKg3P5nBxnDtC3AAWbM33iDp2p1HNtizyS5jupump": "ENTROPY", - "HyDAnhcj7Er5qVHireifnezrSxhYaauFrDNa82nTpump": "EONIC", - "EPepv2vbKu53YCkwx1kUAQ6fnyB4FR3kMBvX4aofQAkw": "EPEP", - "LABSh5DTebUcUbEoLzXKCiXFJLecDFiDWiBGUU1GpxR": "LABS", - "CvB1ztJvpYQPvdPBePtRzjL4aQidjydtUz61NWgcgQtP": "EPCT", - "6ep1sNB9UWni3nBZUX84USsRBhKRzMAxEmcia479cWTh": "EPIKO", - "CSKxPR8FxHXQUE2Lkb8zPFbkCH3315r4d43EM5EWbonk": "EPOCH", - "aheEdmuryJU8ymy8LjYheZH5i2BW1UMsfuWQKD2ondo": "EQIXON", - "HAUWqZTnQNhSqC3AG4GYtuoqTZMi3ywvgqWMyKC7pump": "ERALAB", - "ASRNzwDpYWseimGQh3GootWLphpuMe3cUnpoH4mFbonk": "二哈", - "6MvV5hjy11Szm7HmLciZepMJj3ez9xgLWeFKozFxV1sS": "$ERNIE", - "FjbRf3EcoG13CKrpif9c2BuJuyy3T7r6dsp9LKMvpump": "MONKEY", - "28G7z3VyHDFNhrnvSTf1GssKoGrtCrdiguASQy2BsHiF": "ESX", - "Eh6XEPhSwoLv5wFApukmnaVSHQ6sAnoD9BmgmwQoN2sN": "SUSDE", - "DEkqHyPN7GMRJ5cArtQFAWefqbZb33Hyf6s5iCwjEonT": "USDE", - "EsUEnK3Z26CTCGvFFZb2wLVgUSkYWdKyhZZeBq9P6rz9": "ETHM", - "7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs": "ETH", - "GiLTSeSFnNse7xQVYeKdMyckGw66AoRmyggGg1NNd4yr": "GILTS", - "KTBeXe7VMPMLxBsqDQu4KA9PdSajF3Hkw1y9qRsKqfL": "KTB", - "BRNTNaZeTJANz9PeuD8drNbBHwGgg7ZTjiQYrFgWQ48p": "TESOURO", - "HzwqbKZw8HxMN6bF2yFZNrht3c2iXXzpKcFu7uBEDKtr": "EURC", - "HQMYCZTDq9g3oZejDRUeQsFtLKgyfvBpD3yHaTnain3L": "EUSD", - "EkiZFKi1xYwdWukkhoUmDUWDU6M8bujakCC7P3Nebonk": "EVA", - "4gfNpwo8LQtcgGrNmgWhuwfFhttgZ8Qb6QXN4Yz8BAGS": "EVA", - "GFUgXbMeDnLkhZaJS3nYFqunqkFNMRo9ukhyajeXpump": "EVAN", - "GMt7BjkLUPFA9vjvbAbb94jYTj9gHqEEnUknifuXpump": "EVE", - "CgzdCjj5YNH51uFfQftFbuJKMrwgdWheVjwqjU84MV8y": "EVERMOON", - "EaC6EBUBtNiqZoDEnE3qJ7SDPn6zUzqH6SLzfWWqEVRY": "EVERY", - "8MWLo9PmNiunLFhCZsXBsHqmvKrxTjt3qffFJF81pump": "EVCN", - "CJ1azXTKcixgXVKGGocVkcJAQKytRHjcbMVWoRQcpump": "EVOLVE", - "2jkMKiuKmcuQUa9pdRqSByhGGSwDwLuKuCZUppR1pump": "EXARION", - "7xhB6DFcoRFtY1pHxxkMRbN1MwuNYjsqx4zWqzu6pump": "EXCETOR", - "ARTtpGGVYbVH5LwPNgD7Kta5U4fMNAPNoJcge7iadTL6": "ART", - "7RT5zwZG1hhfparpKgh8WvSnPxJB8FQ529VSyaNKpump": "RETAIL", - "CG73o5Zh2oGKjG9urvpXQR5S3jiSSzkkBf9GomNTpump": "EXO", - "FqveHfaf96iTjA3KgX1W5LWPnLFYv8EfUf6TQSWPpump": "EXO", - "3DBudkWQNbXGAjzEAtpjMKorb76sjozcHCzc8ZtkHDAQ": "EXOD", - "CJRoTbu98waCCuLFfLuJ2kXawLk889fqW4UAAbwondo": "EXODON", - "EXoMAHnw2Gw5fXJTektedLJDVn9vn8tLTEQX7gHccQKR": "EXO", - "XsFj75v4Tw8TWSDaDoVTHF8RAjEEkmUeKgoRjB9DHsf": "EXPEX", - "H7VfCzzPJvnqfs1b8uA9uUuVjXiYfzAUDw5gmyV9pump": "EXPERT", - "E9SmMCvtLfitMwkLzkduwgN8ZYcfepiELLQ8d1SApump": "E=MC²", - "GD8nFZrqEaXkNzPJAmA4ULjMmftoVfBfoGduWGEFpump": "E91", - "qCYD74QnXzd9pzv6pGHQKJVwoibL6sNcPQDnpDiondo": "XOMON", - "XsaHND8sHyfMfsWPj6kSdd5VwvCayZvjYgKmmcNL5qh": "XOMX", - "6MAWnfagDCzqmHQh88FVt9F1zzLqXpwGJpaL7zUTpump": "EYE", - "7x4FgiFfeqzs1tiUNvJzpt47GtLXAJ8Jfn8G1Hyyu6JH": "SMILEK", - "66ce7iZ5uqnVbh4Rt5wChHWyVfUvv1LJrBo8o214pump": "EZ", - "GSwzg99Dyinar5oFsKT7WW5E4XJDw4ac6tPwM2bUM1WK": "FACELESS", - "FuVJN8aHZrBJESbX5aq8tvNEWaBucxtq1e82ja1hpump": "JOY", - "FaDErpochTz2uCFo3ZSGxEfrXaHoK1kzjTRYFaegG4Lu": "FWT", - "BP8RUdhLKBL2vgVXc3n7oTSZKWaQVbD8S6QcPaMVBAPo": "FAFO", - "6jEp4iQeLYJuxF4vUeiESsVJH1L16HhjAnuyQrz7pump": "FA=FO", - "5gsDMgG3p1xZR7qvbS88EjNSDesBdJjYH7PGemUgpump": "FAHHHH", - "s1bYcu6WSfRznQbEn4tnXaNVHRpWPzVz8mPCvHrpump": "FAIR", - "Fairr196TRbroavk2QhRb3RRDH1ZpdWC3yJDTDDestar": "FAIR", - "A4axyAWizMWvgk7tfPC9yrVBYCpdM2Aoh5RaU7RKpump": "FAITHCOIN", - "6mgqeeGHE5GrVk9fYdeJSjKTFZV1TVNAQTMYdHjfpump": "PINHEADS", - "LVCQ1YmeK6DECKqcwmscyDRxUYJpiGth9ppGKtopump": "KNIGHT", - "3XHafP5M87BvqtwCYBRGvdKLQfrFtCh4wXzBQ5BKFLAG": "FLAG", - "Afo4NumBNHDXc7m7p6qjZ1pF3LbqYfG5k1CNrGve8rVu": "FALX", - "7njsg9BA1xvXX9DNpe5fERHK4zb7MbCHKZ6zsx5k3adr": "FAM", - "FoXyMu5xwXre7zEoSvzViRk3nGawHUp9kUh97y2NDhcq": "FOXY", - "J5CpjMaYHYdZu2WBUPT79mALDJM14FKCiVVPwUDcpump": "FAPCOIN", - "GvhKJQGSLsPN49AM4cHS6YSDZysPmP5zq3E3Shwubonk": "FAPCOIN", - "8vGr1eX9vfpootWiUPYa5kYoGx9bTuRy2Xc4dNMrpump": "FAPCOIN", - "BDciduUgepb9DehoJ1CDK1gqNkUUKusQP7PwHr9cTwX2": "FAPTAX", - "FcpeGHBBpMrgdaiPT6LJNSxBE8Y5u8BDb62CrFVyWNDD": "FARM", - "Gvu9p88rVTRTdzz2UzMYkKRobfTWDyBHJGH5G29boop": "FARM2", - "y1AZt42vceCmStjW4zetK3VoNarC1VxJ5iDjpiupump": "$FARTBOY", - "B5X3YLev9bUkLuBJsjA9vMkpRxSLDgStDwofBLvRBAGS": "FARTBOY", - "3dk9CNre8tmv6bbNXd5F6dgkNnEzsyQ7sPhVT8kKpump": "FARTCAT", - "9BB6NFEcjBCtnNLFko2FqVQBq8HHM13kCyYcdQbgpump": "FARTCOIN", - "EJUbsyWDuJvnHSWQYXMnqcqD4XefA9WM4ftjZtSmpump": "FARTCASH", - "2ZqgkCpvXjzB4NstzBUPSrsYrrbziUv5mC9CsxSQZryG": "BUTTPLUG", - "59Vaf6t5viekuPJ1ehPL3hRC8DXxPGLTFfM2t8tgpump": "FARTDADDY", - "9jBxPfYJmaDuvpWT3b2J194NYrBWksxhNMZxvi31pump": "FARTDOG", - "493AddQQYfn1i8fUN7QtMupfXuGHehzzTKVVQXZD25Y7": "FARTFUL", - "Cjrrc595e9R47EZ3gkbUmL6ZZpLSxiXg16v2Y3Aapump": "FARTGIRL", - "HS9MCtW3Q2R3GV2fjPTkSL66tGfNQaSMvjuSz4Hx5wCP": "FARTGOAT", - "3siTgKmX1oWFvKzKtfbNKQ8MFq9kRnA8wQodhbwYpump": "SOLANA", - "EzVkdRYRACWKQeY7fHg4BBL7gx8WC5DvNxWm4ZA2pump": "FARTGUY", - "2RA1v8NdkEQcF5N5zHUqLuAHxjnDMQFjwEE8fwKNpump": "FARTHOUSE", - "5S82DHVy5s5TcdaACov9an2qHcP9Z68ZGuNsRDPGpump": "FARTHUB", - "CDDybYjY6y7RBKXXaCKHRhUhb31y3rjGB7idTdeFpump": "FU", - "FyrBf5xKg5EwKZ9pHvSpJeLLuCWBicTpm3VvZcsibonk": "FARTLESS", - "E2Kddk65HwvevojHnZgQaDE8Pbd3e4FZrVuEz31Npump": "FARTPIG", - "FSTRgYfDaudjDwFg5A9LQpDMq5vxqhskHq4rkjUMwERE": "FSTR", - "9zFU5vR7YNBzZU8HwJLUKjoDEonpgvHUdLbMgBa7pump": "FARTWORM", - "7bXRKw7pkr4SNgeSyJyVQNaMDYvnCgCr2NwRcWJNsAaM": "FASH", - "HQAuXaZMgRg3y29Yc6N8BezZWZnP6Tk8FJdKmxinpump": "FTW", - "DXCoKQ7iLpux398fNHewQn6djfGobzFuPiR5o8hrVHAb": "FATALITY", - "AHdVQs56QpEEkRx6m8yiYYEiqM2sKjQxVd6mGH12pump": "$FATCAT", - "4y9E3tJpGNzRr1592oWTPECgyp2VDSc1Bf3DqAm5FZsK": "FATGF", - "3k25pZPecCgbkdPt7Q8k2NhRc6LtM5hg3HHfdz6iPnxA": "FAV", - "54FTD2p5YW8iTrubQKiQ2DDa9UdKiTYNN32D7mAdpump": "SWAY", - "9NiHcbT8EohKJid1RdU3232tATmM1CvYemTeCP5iwro": "FCOD", - "DRaT4y4f55shDuBnpr53EZs8jKnDWAnrJifjGLkAoYxa": "FOMO", - "5j2shkkzascw33QpW6RRqfRkacLkn7QwwV1e7jX36nyn": "FECES", - "a4WcyMS183n6anrtQ5r9i4YVX2VuT7b2h3NywnEBAGS": "GAZA", - "J2eaKn35rp82T6RFEsNK9CLRHEKV9BLXjedFM3q6pump": "FTP", - "7QnHsqzjRDFSq4YwJRH5RiHh9SCNbH9k3UfubsmRyYWX": "FW", - "aL1UNC3fZpUSQHsQAe5id9A1Ga3UNCZsnxjgUiaFLiQ": "FLIQ", - "GMbAink1cNVWGa8uH9k6t6Y44sn6doD3BVH2usCP7EZV": "GOOD", - "FnixeBNA3YXU3JUK9gTeHjDv9t36YGUyuf5Q9od7tTVV": "FEENIX", - "AqwCfpkG68KgpXXKT5PgnuUi6YJCAcHRtEQKXN9LBAGS": "FEENIX", - "7CPBU6CL7GpjzM2DRr7HVoy7TGJxvHkPHWxfvEZypump": "FEETCOIN", - "Fjk4u44UQFo3Wb2vhPXE9FSjngP8uNJJd6Jzd5JJhD6x": "FEFE", - "BychPBYgrmUeRE38qKqs6Tx4Zo5Rh96Mcp9Lu8PUXFVv": "FELICETTE", - "7DptUgc1aRY6geK4WnauRKTTfVFWmLsBWP275sD2Bg9r": "$PEOW", - "6NKqYaVGC7H5cyKekgPMeHrb1REEXGEeBcpxqWc2g8nc": "FELY", - "65Z76ENVtuTVohmGeSMVAPW7ArRoUByZspCN3Yb7k7p": "FENTANYL", - "H3EY9q3rVxnUe56gF2YX6SJ5q8YAUbyNADyc2xPLpump": "FERG", - "DZq1fdJfGjv27Cev8hNFZXJwhApB3xHpo3KMTTRvpump": "FERGANI", - "6FLqdVizDf1ESwCanSzAtysS8pQeqzDDmdieADgBpump": "FEC", - "HUtnmegDSBQ823ceAaDX6GemXZ4UUgy2eNLnY5Tepump": "FETCHR", - "J3u73qgyeuaeunH1uwsT6QLFa24YNeZSjSEdCjAebonk": "RAGEGUY", - "BJhPr9SM7uZTZXHeSLYmUk7CjGQq1esFkVxPF5tondo": "FSOLON", - "KMnDBXcPXoz6oMJW5XG4tXdwSWpmWEP2RQM1Uujpump": "FIGHT", - "8f62NyJGo7He5uWeveTA2JJQf4xzf8aqxkmzxRQ3mxfU": "FIGHT", - "aLDdFsr3VTUQaHFK6yNvQxztvxQ8nxW4AMuSGC7ondo": "FIGON", - "XssfYT48PeXG2EmNdEaVtQcveNhxYnM42KTqdjmiBS9": "FIGX", - "ZmHxc6Gt27RJKxD2ay6UL4n9yQ7mKAq4XZQUeVhondo": "FIGRON", - "8SkfuQkYNTskoQUbbjr2JbZQeqQV9egnJXgfMXf5bonk": "FIH", - "A9e6JzPQstmz94pMnzxgyV14QUqoULSXuf5FPsq8UiRa": "FIJI", - "AKE5B588aZgt8WVioWRUVSp7XxdSJsz9GRDErC4ua3Fv": "FILTER", - "AU1rxhY276GepM4bm7VaYrgVPJW7tg4z2MtR2jfPpump": "WIZARD", - "758yZPp2QEmrMgMACiUS2K2sTLsfSw9NprWoGxdxbonk": "FF", - "CLFR99t87xxRQtEtkWkrGj82BiJ2228h7JPgkLWiZ45o": "FINDME", - "8bmDcRBjBfcoAtU9xFg8gSdUzvjK85cBmdgbMN9kuBLV": "FINNA", - "Xp8dC5i7EcAeZo873ijwvipt3pUbgvgi5sHsg9sBAGS": "FINNPUTER", - "5XFeFezwgZvSdG2N69FPBuz98uFpfBCkFcXQXSiGpump": "FIREBALL", - "BHKVuBNfsK3Pre1bmfG76NbVt8xrx3Z5DdFtrsoVpump": "FCKN", - "9NyeEdqoQbvZXdyam2eTwXGNB3JgkcUtTexSW5yJpump": "FIREHORSE", - "7Eofgc3D5G3CJf2s6NXxCaFKN7ZE9eEauGPBL11dTqDa": "FIRE", - "BD7jNnt6uYDp9rVRBTjJhkweGxYQppRe8CXLnAJspump": "SNARC", - "AhdriVFckrSmt6xfXVdNcKA545bKvKgJQuk1LAnApump": "TILLY", - "8nzCP3xmkpKAq2un87d6Jgg4r3JnvgUSkFemfLbFpump": "ROBOCOIN", - "RdTSEwJwHA8vh6BALTu9hYVYv4FgWDS5JsK9hnApump": "TPU", - "CNvitvFnSM5ed6K28RUNSaAjqqz5tX1rA5HgaBN9pump": "FRED", - "APMrwq6jxn3yvHJ7BWsitpMjZV14MCzgno8pEHzgpump": "FRED", - "9zNQRsGLjNKwCUU5Gq5LR8beUCPzQMVMqKAi3SSZh54u": "FDUSD", - "BE9WCtE9p36RX4kmsN5VDd8MSc5QwJQcARbSL3R8pump": "TOSHI", - "9AgTccePC4WyQQwJu9GYqAVUKyUyh1prgzkacMaCpump": "NEKO", - "8T7RjdERSeQNqUhthUgEEUr33g6xdVVxND5YmgjSpump": "KEKELS", - "6T44rfi9BDUdZbEvVddZWVfsGrpC6N1sSSKYnCsLpump": "SIRIUS", - "ivBnfPTyuHDNWmMSnbavckhJK6SHZW8h77nZKsEondo": "FTGCON", - "BVdL3WUxtxUD4vXRWwqChJLbGxvfzZjBGPp63Wtondo": "CIBRON", - "Bz7Nx1F3Mti1BVS7ZAVDLSKGEaejufxvX2DPdjpf8PqT": "CHIPPY", - "HM1qfMccuwNsjPTLUY4bHiYF3bSYzn65hZ5vH1P2MiLL": "FSHWHL", - "Fishy64jCaa3ooqXw7BHtKvYD8BTkSyAPh6RNE3xZpcN": "$FISHY", - "CHiNsA2B6ZbmKnEmHPCbbX9aXJyoJzAtcLpHEDd6Qyq3": "CHIN", - "9kvTPjemayUL7XKPyjhqavbcLtY5VP2ha1G5vPuppump": "FITCOIN", - "Cr2mM4szbt8286XMn7iTpY5A8S17LbGAu1UyodkyEwn4": "FITCOIN", - "Hvcmd51zk2vVkeAkYbNgSKsAX17hof2vLsihG9J2pump": "FITCOIN", - "EgrWay6HeQV7VeZ6CiKuLaMJDESZa1woKuznhyTEpump": "FITGIRL", - "G1FhY1k9S4Q8P7zpuaQAnzZ1YGwwyiVRQF7EHncMpump": "FIXI", - "GWkA9fwAtJDb8BfBAabRY3w7x4jQrboLZd9iWH1v7yuB": "FKETH", - "BivtZFQ5mVdjMM3DQ8vxzvhKKiVs27fz1YUF8bRFdKKc": "FLAME", - "3vADYnNebwAzPGphyYAoPtCXw1H6t5yFFbCCy4JBpump": "FLARE", - "NUZ3FDWTtN5SP72BsefbsqpnbAY5oe21LE8bCSkqsEK": "FLP.1", - "H9BkzwHKyBAnKmaxuLPKKRZGu8whbigdhhqQVkRapump": "FLASH", - "FAFxVxnkzZHMCodkWyoccgUNgVScqMw2mhhQBYDFjFAF": "FAF", - "FRCncx8MiY2QAVj44kcsR8yD1QoF1WNuokKGhNj3jPeg": "FLAT", - "3HYx6a9whu5a4dnzE62WNXg46MrEmu9LFxutR2YBpump": "FLAVIA", - "DezaX4JqtoZ9TdUZ5eGbPtQtpzkQFDERuWUMFgnypump": "FLIPCOIN", - "JCBKQBPvnjr7emdQGCNM8wtE8AZjyvJgh7JMvkfYxypm": "FLIPR", - "DWjVPqEX4fPFQ47Xb7EegpGhgWzRviYfPoEWPnxz2CRd": "TIM", - "9XkR8j3ygnygzAcQaURpyE9e5kDb1gqAbJZxAwzZpump": "FLOCKY", - "EcdKnP5PsjzWBzk4Bmqzp8VYi5L7NAGEPM6wK5K7iW2q": "FLOKIS", - "FY4rNz7AqKEnEwyurmGVpoEBnSq7XqNn4YuXruvJhAXF": "FLOKI", - "3jzdrXXKxwkBk82u2eCWASZLCKoZs1LQTg87HBEAmBJw": "FLOOF", - "X3rigWZSJhEE5DCQVWhnEChpWkJyHfidwXUSQPBpump": "FLOOF", - "75DvRjTDpaEbrdUHVomdjfsiXbBi5FwNyctyUgfLooS": "FLS", - "CQNZNo4bNVhsnwz8EfaYkq43xFjrQqXjPrYUo4V8LxU5": "FLOPI", - "EPbztGmoacJ2ns3pYx2FX4xRo3qD1MsnJCy2Rsdopump": "FLOPPA", - "F6Aj973E5vGHyGnVsKC39rAFwnFukF11JmpPQ7xPUFF8": "FLOPPA", - "CnGb7hJsGdsFyQP2uXNWrUgT5K1tovBA3mNnUZcTpump": "FLORK", - "HDzTPAndwe5XRvReLzLdUNaNCkbVv141ELZ2AGKGCpAA": "FLOW402", - "HEHT1eKNsTnuMAQaSM4ac8r3ynckmquxA3hUun5Npump": "FLOW", - "AVyjco9j8vv7ZPkhCpEoPJ3bLEuw7G1wrrNt8DrApump": "FLOWER", - "26QT2xABFLvK7LyAjF69xojVCnJwexBGj7WfJaRbpump": "FLOWEY", - "AjPzK6Sf1G27jFkFe4HViSNqMxa3JLE4D1fm6Pzouq2q": "FLOW", - "Eh1fXbAipe4k7CYR9UMb2bbWmBcpU3HcyX3LWuRVFBLz": "FM", - "GYKBbafCYFwd9gYrZuVQ4yYwdHgbxJ1HRQh8jN6Apump": "FLOW", - "6CEjCg7Jo5RV9kFSgKx66rpW19nrsCmccD2bxfwpump": "FLUFFI", - "CoRkC3r6MqYuTeMRc7D8JJF7UiUyFWurXGpYy1xQATNq": "FLUFF", - "Ez2zVjw85tZan1ycnJ5PywNNxR6Gm4jbXQtZKyQNu3Lv": "FUSDC", - "FLUXBmPhT3Fd1EDVFdg46YREqHBeNypn1h4EbnTzWERX": "FLUXB", - "BCXpjsHYmgVpRKdv4EQv1RARhYagnnwPkJjYbvM6bonk": "FKH", - "FXDXLCWYMXmhhMypVxGHCC2sHA8n9d2fHhePViAYpump": "FMC", - "3BHhMXMyyGGzLcTk6u5iJwTcV7eEGG9bWJrTKUttpump": "FML", - "5qsZAnWuR7vMJbiznrJEY2ddVqZXjNYmFvR3eXSxpump": "FOCAI", - "6bdTRHhdZenJQYLTxaYc8kH74GBNP9DoGhPnCjfypump": "FOG", - "CxiR3c9AGqMtE7bg82sLnzpLinXN3kXfcoYeYtGApFoG": "FOG", - "Cx9oLynYgC3RrgXzin7U417hNY9D6YB1eMGw4ZMbWJgw": "FOMO", - "ZxBon4vcf3DVcrt63fJU52ywYm9BKZC6YuXDhb3fomo": "FOMO", - "BQpGv6LVWG1JRm1NdjerNSFdChMdAULJr3x9t2Swpump": "FOMO3D.FUN", - "B4gipVUHKJ7keVN3MWEYDQCHjkdoXGsbmZoTSJDZpump": "RADIO", - "A3GCAAwFxAUgx97j9uw8Vkf8MaFqUut2tsyAXq9Cpump": "$FOND", - "FTkJ6EjsTLpmQUU9cgbtHjhXuBHrwY6UGRsrgWKtpump": "FFG", - "EcK2evV2cDECVsmvY2FxU51eu3fp4w48zrZxuA92AAAN": "FOOD", - "E1hxSfVi6pQhT6tcyq3FT4sEhU9jWdxD6MEtJpGDpump": "FOOTBALL", - "8cn4JeRLiHTtfX6maZAsipGGyyZPdEcos3s2X3Hw3BS6": "FUN", - "EUdQbKfHucJe99GVt3HQMUZCQtWtvXFyjvrqDWctpump": "FOOTCOIN", - "5hT2o25X9tGXipwhLckaUdgnxrZ6Y8eiUwdhpLeondo": "FON", - "BoAQaykj3LtkM2Brevc7cQcRAzpqcsP47nJ2rkyopump": "FOREST", - "5cQtbxhPTVQJVKKiQazpmQpSZzEJaZ86F55sFpYvpump": "ALONE", - "8AdsF2QXFA8QkFuQ9TrQW6nEyM4E6yT4AwqgZ5cTpump": "ALONE", - "aSCcjVmMJttZQNYtzhFXmqmUiTm8xkJBggn4sj8pump": "FOREXLENS", - "foRgE7d5FRtvQ2jf9yhV9Cnd1LMB3rxk6dpE7HYdzGk": "FORGE", - "GqcYoMUr1x4N3kU7ViFd3T3EUx3C2cWKRdWFjYxSkKuh": "$PURGE", - "66LPmREAYBqPeyidL7LMW7RaJjKbfADbuUMsqatkpump": "FORK", - "Z6RHtkv5xrhpHwswaHSP6W1GQkhgqH3fAcA4NHforma": "FORMA", - "HZ32SiTtw3kYyaHTtTfpHVF8EyXFcy7MBQXeFpnNvQ9c": "FROG", - "DtgCVZ877swr93rHXhbfshcthnSK2Ds4dz3XC8GYpump": "FP", - "7GzQgf6DPo6ZANjnbhe9tNCpkGTv3zqHbsDx74jyQf9": "FWDI", - "3hRfeCUc3NuSEuZfsLBpi2X6AUcF3jRKbCdPrpKpmoon": "ONE", - "2ZiSPGncrkwWa6GBZB4EDtsfq7HEWwkwsPFzEXieXjNL": "FDRY", - "3roGjzv4TeaWFN8VN6hfUKAsmoVUrYmmB2Dz3NYF1GAA": "FINU", - "CgGWS19zR5xTzgCEcW5Svsuon4hBZwzBwUFimoJStCf2": "FOXSY", - "FRAGMEWj2z65qM62zqKhNtwNFskdfKs4ekDUDX3b4VD5": "FRAG", - "F9mv7XXbrXZb1sP2JUoswbCB3WHQM4QGMFDTVfnRZMnP": "FRA", - "AYhFJk9ZyKN5aCRwrG78iTvuxnrrLp5q4fGfyBM7pump": "$FRANK", - "CYAwMGyuNSDu7NpuccNwcxMNS5Bu9akxU2Jooyiondo": "FFOGON", - "CZ3FxxSto7tsjkSkqMek1C5p3RCFFmkwKqW57nbondo": "FLHYON", - "D8KT4Jd8qiKKTfkM8ejSKCpWGR1o3GFvnQGp5ERondo": "INCEON", - "CYqLHM92EhmF83iNgfN4A1j2ckjsHigRvXu7xHCondo": "FGDLON", - "5Tu84fKBpe9vfXeotjvfvWdWbAjy3hqsExvuHgFqFxA1": "BENJI", - "CZ9GBn1okotqKNUUqoxk4PF2JVi59bw5GWvVo6Dondo": "FLQLON", - "6LX8BhMQ4Sy2otmAWj7Y5sKd9YTVVUgfMsBzT6B9W7ct": "FRAX", - "GzX1ireZDU865FiMaKrdVB1H6AE8LAqWYCg6chrMrfBw": "FRXUSD", - "5arEQv5tGJj8UbhnPrc1WL5KMmijCe4FuexCaEXUBAGS": "FRED", - "GYyyeA8Pzp1Biyzp26fvFXaB3Qm43o8e5CfXseVjpump": "FREEBIRD", - "FrEeWJreTHUyPgdJ3DWrdRg6X8qogF5zG5qdxMCgXtTy": "FREE", - "moonskJwNmZiwkqH79S7b1UezGTaWQGEUbFkVqH3Vwq": "MOON", - "CY8ttw5rYCT6fFBJwqXofefqa7Ji9E8zfLmhRLmondo": "FCXON", - "BpAiFPCqjvnz7ETKjxr6ZpEKKnGGBE7rNZUU7A7eBAGS": "VERDIS", - "AyxrBmqbzf169oe14XcSDMbgpUV1ntP6thcScYpYpump": "EAGLE", - "F9GqoJRPzQnGzvP7cQzLHB7C22DToHQYWfsPvhKwqrpC": "FREGO", - "AzVCECC5zr21ikYQne4Xtoh3SVg848duPHUHb3M7bonk": "FREYA", - "EsP4kJfKUDLfX274WoBSiiEy74Sh4tZKUCDjfULHpump": "FRIC", - "D4Yc5xRjRYcjxtnYG4Ay3mKLYMMF91RBnbuoKk7cpump": "FROG", - "sm6LoSiRQLM28rMgcd8GtR5XBkZFMEYXgEUgwCxpump": "FROG", - "6NspJqVFceCiU5D1YgVq7waYoC394Vhqxwg7cSJdFtVE": "FROG", - "peen77qWZw4XQkvxW1QF6MUJyKNLbqvMzqhkKpB1aVo": "PEEN", - "5yxNbU8DgYJZNi3mPD9rs4XLh9ckXrhPjJ5VCujUWg5H": "FRONK", - "FRNTPi9V3Sw9b9U8d5Q3WY7tNANT6Q394d7dYtv7Jdog": "FRNT", - "2pngLT6YDApMFLV6uzhffDLR4aLJXTD3AenpkJNrrZZB": "FROSTY", - "AXzueZQiCjJStGPuGTYt1jyJdG7pcSwjVorWmpN9vm1i": "FROX", - "GmTH6V13rHVXeNdmDfh7VQeS2Lj9YWRKT5XjDEA2pump": "FRUG", - "GP7m3USdHDSrNoUzsZqZTboKaJiabFQShzgV2RkFnZyh": "FSJAL", - "8Gzb4Le3rUkDepM6xtaj7X6cN1xxbrHNXqtMb8Pipump": "FTF100", - "AGFEad2et2ZJif9jaGpdMixQqvW5i81aBdvKe7PHNfz3": "FTT", - "4P6gQYnZ2AvwmAr37GCeUq1vyk1PQJmXnuA1QFRKGHkz": "FUACK", - "GyK8nknBB92EEEq2LEyUbEcDwjZ86eF98m4Kbrmgpump": "FUDNA", - "Hm5icDFBPVyVnqxX7VADnykZw7j55UP6ZSuMnyrFttGU": "FLAME", - "76Jkgbh2v5pdqBvSKStyxL7Q135dDcuh4eatCFvpump": "FUG", - "4M79Qjv2Jfjmcq19M41V8QFYohBv2KrgEn9dJhVtpump": "FUJI", - "2KchKijPuwnwC92LPWVjFjRwB3WxKtzx9bbXZ7kRpump": "FUKU", - "n669kahBTdMjYFgXEfQcc25ZbWur1Jz29csDzPJpump": "FUKUROU", - "AshG5mHt4y4etsjhKFb2wA2rq1XZxKks1EPzcuXwpump": "FULLSEND", - "G849nDx4r1vwjibbmpjkZ6pbDWwaMouhkWLq1o8Z5FUN": "FUN", - "JjGQAsJBRQLYmBj41bgZggVLawaa4qoSF77NJsRpump": "FUND", - "3u4XvCjmTmQmcvjoxo1z8y3wnRdtnCuv6N9WkiE6FJvH": "FUNGI", - "FW1r2jQtFjc1dFUUYYZDkeQ62R6fWo4ucty8vpP7bonk": "FUNLESS", - "Eqpvgps4py3nDkgvRnVGv7JsSwBF3oa3i1Zvaszqpump": "FUR", - "866Sh46xjH7cW7aW18tBUmGm3xh6EzGTk1Li7YbbmqJr": "FURM", - "DQc6FTgu2HiB1PMUkHj8SATCtxsCeeMz5hiaqwk1pump": "FUR", - "3naMj3jj6877gfXy6VC4fhaUPkwZ5Y7y8UWi5QeXbonk": "FSR", - "Cbjr1Nvcay3QWDriyRKtokJ7V4PMknesGxeK8z7Zmeta": "FUTARDIO", - "Ao5rKFRQ54W3DKSAtqfhBRPNHewwWRLNLao2JL9ondo": "FUTUON", - "82mSw24TxU8Q7kRkXMj4Ey3jaHKLYgWxoRdEA256pump": "FSX", - "9v9gJt4EymZh9iHLpGGGgX5eXQFMsmg7GKR9Z2xfVeix": "FUUU", - "A8C3xuqscfmyLrte3VmTqrAq8kgMASius9AFNANwpump": "FWOG", - "92cRC6kV5D7TiHX1j56AbkPbffo9jwcXxSDQZ8Mopump": "FXN", - "7YHPdtmjuEmgEA9bTWAWNSULacbjADxEKENL3XWL1RXC": "NOOSUM", - "9uTekAxmx4rqBcR3iGkKmaBwVWMmi7TXEU41frBdBAGS": "GABLE", - "2EH5Y18tw366g3VQvXKXKhr4zhYD6sC8X2Ktw3ocpump": "GABRIEL", - "76VxJbMFoCXBcn2rqPQX2UsUfpupq2gVwGSq5LyYMhr2": "GMRX", - "53dig5bMmYBGNxDXAGFNjA59mqry3piqTpbFzYKEpump": "GAIM", - "FFgfStKwuF3DSxEeogA69FNkPrkb7XDA5Tw29TBEpump": "$GNZ", - "96ugdN1zEe1RShH1xWsMLBbopDvS96WXfvxwRELqUwqk": "GAK", - "eEUiUs4JWYZrp72djAGF1A8PhpR6rHphGeGN7GbVLp6": "GALA", - "3ZELuj8iVdekxTR5a4SVG6VkgR7pHFYTLvyuLJDURSs4": "GNET", - "AjdCzXwien2FmTx3GDZeA6YX34A6rzhQCCZXSa7tqXJ4": "G-BONK", - "6Z17TYRxJtPvHSGh7s6wtcERgxHGv37sBq6B9Sd1pump": "0.000001", - "9RzDmj24vh74z3d5mYvtyxmGjHUKNUYfvQGh8qxVpump": "GALAXE", - "2HehXG149TXuVptQhbiWAWDjbbuCsXSAtLTB5wc2aajK": "GLXY", - "CkWmEM2J79k6AjAwyQVHXteFucAL1zQrKLxLqJHondo": "GLXYON", - "Xs3c2aZenyRQwXjki5MDxJEJ2km27ef2rWQMFWx7QKJ": "GLXYX", - "9FyERA7vjSDXVEGDskjYjTfymQ7XTqJt6dbVDCWmpump": "GALI", - "FRsWUe7PUjWgxitDxFhURsHZuemsZwXtqT5R6QvRbonk": "GALLO", - "3wtnog8YQ4zoY13p8tqwgL9GWz2fxNyQQVJY2kSok7au": "BALL", - "GKZbA2gDzw3MoxbfRsnrJTNi5uBDrnrz9bq1pNnx6kv": "GBOY", - "8ULCkCTUa3XXrNXaDVzPcja2tdJtRdxRr8T4eZjVKqk": "GOME", - "Cy52Ts2GwSzdkhCihB5i1Vu6sApzgqktNNFyHbsdgwm7": "GHX", - "aznKt8v32CwYMEcTcB4bGTv8DXWStCpHrcCtyy7ondo": "GMEON", - "Xsf9mBktVB9BSU5kf4nHxPq5hCBJ2j2ui3ecFGxPRGc": "GMEX", - "Dz4bX3snTDxqdKyZwdUgKoDvSjyvmoA23E6j5odZpump": "GANY", - "CKaKtYvz6dKPyMvYq9Rh3UBrnNqYZAyd7iF4hJtjUvks": "GARI", - "8c71AvjQeKKeWRe8jtTGG1bJ2WiYXQdbjqFbUfhHgSVk": "GARY", - "5SRer48NRfmhsut1n4ZwSAVUJAErNjdKcXTMVaxdpump": "GARY", - "CUb1Jek3hTuQ38dTTDzQhR78PF4w3naMyG4UgemPpump": "GONESLER", - "25E8X8fPnT9AdU1nA78snybnzVxkDvv5rK663Ngrpump": "GAS", - "DokPYQ33k3T9S7EEesvwvuuAtoQb4pY8NWszukKwXWjv": "GASH", - "7pskt3A1Zsjhngazam7vHWjWHnfgiRump916Xj7ABAGS": "GAS", - "gateMurAxe4YFoUR6J63gXGKtkbTfdkMdLjZrCmThFP": "GTSOL", - "gtBTCGWvSRYYoZpU9UZj6i3eUGUpgksXzzsbHk2K9So": "GTBTC", - "ibRLJrmgVuZh3tdDpjGgU5CQCCxpxuer7B7ckjGdLsv": "IBRL", - "9mQEkFVqmRJLMPUJT25qriKXi2sH8RiuMBrzLeLupump": "GAY", - "svy5ErijNYy9hEVzxknCdwWdZ3NeXJTdpb9Ndnso17f": "GBOY", - "791hZNiCJy1qGSGzAvqUU8X6gejiBJ2mBV8JjYoVnzBR": "GCR", - "GVmtuUyLDuXNh2gJtHgarEtpqSiwn5965fNv37VeEhk1": "GDOG", - "EbW1bx4NdjH4aTTUNAP9PRBYTu6qJE12d9vUhvHbpump": "GEEKED", - "NUGuzrXB5JwxBAhTP8QHa8aiNBN8suoWiN7wiRppRty": "NUGGET", - "5zbUuCeYLHVApzANWNSXkKwcEoZsbMghoh8z8HbWTJbJ": "GMAC", - "ARiZfq6dK19uNqxWyRudhbM2MswLyYhVUHdndGkffdGc": "GEMINI", - "NrTdGMA3ujUvWXkwXyZKnhoByb32KTjRh5Vo47yondo": "GEMION", - "9UEN2kKhHxnaK14ZYFvog2hezajxAke9xCiS2hnppump": "GEMO", - "68YY6KjfnmPuQubYkFTsmXGaF2FuwcxumWLFxb6cpump": "GEMXBT", - "CftYJVf5mXhuHYjS9YQSK9z6q355RukjPZ6ZCsrw9sRA": "GENCIT", - "aTBfDuLRqYHBiG82bHA7DzwjSDTFre2dRtGH3S5ondo": "GEON", - "Dzox5Ytp6ZU54h3uutcp4MXw3ZnBxR21H5Zx4r4qpump": "GEN", - "sxv1symoD4WXjpeXCs5USFEyt8hBhmCuuptLjA8uRNy": "GEM", - "GFRxeCdpomjJaYQEuwPDAPghyBD3H9zqnWY2HJPjpump": "WEALTH", - "7uerT1tMCury3t6HHhAMbBmq7t75dP9H4T9ug8AKpump": "GEN", - "7EjWHZwtstT21dUniWpvVPRrw64cmX3UYFE5crJ8pump": "GENESIS", - "SHDWyBxihqiCj6YekG2GUr7wqKLeLAMK1gHZck9pL6y": "SHDW", - "3Jjt8QhbqNoYfSQYHWf8ZsTJwE2CyvmUrzgzJD5Jpump": "GENIE", - "24kAN3xiQZba11BrgEsM9qfcqkD9xM7fBxnizkENpump": "GENIUS", - "GENEtH5amGSi8kHAtQoezp1XEXwZJ8vcuePYnXdKrMYz": "GENE", - "3an8rhdepsLCya22af7qDBKPbdomw8K4iCHXaA2Gpump": "QUANT", - "7JA5eZdCzztSfQbJvS8aVVxMFfd81Rs9VvwnocV1mKHu": "GEOD", - "6nR8wBnfsmXfcdDr1hovJKjvFQxNSidN6XFyfAFZpump": "CLASH", - "v5g2uxKKpSAUXkCw8d51A2r9jvBJLDJD7jFPkTSpump": "GERALD", - "52DfsNknorxogkjqecCTT3Vk2pUwZ3eMnsYKVm4z3yWy": "GER", - "HqZrimiQXfHFd6WmBUrucLrnFHpApchao6LtUL3vpump": "GERM", - "DLvWoNT1d5iSX2T1aUUBzRdGbSef2xW3MwJBKtewVW6h": "GERTA", - "DpFkEmSqYBd2vq1yPHa2awRGwYT55ksAczpF9YepBAGS": "BAGGED", - "6V4QLouv2HTbae34hBjLgW5Qc3kdquqFLBsDS3scR1CH": "GRODT", - "5oUzkFsCFMJJ23Z6Ghev5m7FjE6TtrqZJXUS7V5Smoon": "RICH", - "8116V1BW9zaXUM6pVhWVaAduKrLcEBi3RGXedKTrBAGS": "GSD", - "4nm1ksSbynirCJoZcisGTzQ7c3XBEdxQUpN9EPpemoon": "GTG", - "CgZSv89BL58ybWfWobANKEU8nV9jYfFw23G2DZEondo": "GEVON", - "5cerJBJD7q3xfi24JVxSoP8AqeavuUatJtrfrXVokZR3": "GGEZ1", - "HbxiDXQxBKMNJqDsTavQE7LVwrTR36wjV2EaYEqUw6qH": "GHOST", - "4TBi66vi32S7J8X1A6eWfaLHYmUXu7CStcEmsJQdpump": "GHIBLI", - "9WyRszmxLf1e9nWAVf4p7j7S2ektkLu74PTLVVKLpump": "KAPIBALA", - "Exw8YLyWbZfF9KTVhemcJrkSr7abUQCa7mLowgj3x9EC": "GKN", - "3QYgRBYG8qQxKq8wkwrv7xZZATn2wozeVW7C7Aw6fREV": "GHOSTFI", - "BBKPiLM9KjdJW7oQSKt99RVWcZdhF6sEHRKnwqeBGHST": "GHOST", - "Cc3voFEFTrGmnhnP8x77ncZ6pivG8Kk9ir1t8AhBBaby": "GIF", - "511nS6sEQ1u6wFRSBRuG5ip1DeemqUofzy2V18UDpump": "GWIRE", - "GpEKud3JpJDc5D3Gek8UVCb6vAiahGmDUZMQFnf5btai": "G", - "6FtbGaqgZzti1TxJksBV4PSya5of9VqA9vJNDxPwbonk": "GIB", - "GHpAbHZ8MCAXWLdKzxM1ZGhP2U4u1ni5vUcoRSghpump": "GIGA🧠", - "BcDFqj8BoqLKytNqEC8QxFYVbD1mS9FThoVT4vQbonk": "GCAT", - "63LfDmNb3MQ8mw9MtZ2To9bEA2M71kZUUGq5tiJxcqj9": "GIGA", - "9iCCVaGu9oPcVczW7F1hNQmbz8q8LJwivQM8aXSPpump": "GIGADAD", - "GigiN7pb33qVqEebrAdghhYsHm477wdC347MPgyCpJ7F": "GIGI", - "7wdxpzhn8Bz21nTb9b7qM7HzmU6LEoV1QeFKqWeKpump": "$GIGAI", - "9HBoGfJ8TotjjJQ6KwqMM8iiC7xsKYjVywfb53sxpump": "GIGAMAS", - "BgWmyRbcyEKTChUoH2xbcsEdVnJezs3ckveMxADbpump": "POTUS", - "6q387cQFB2bobtdJGAMVVW5NoVL94KKmHXmEPgwUpump": "STACY", - "Bsow2wFkVzy1itJnhLke6VRTqoEkYQZp7kbwPtS87FyN": "GIGGLES", - "3WPep4ufaToK1aS5s8BL9inzeUrt4DYaQCiic6ZkkC1U": "GIKO", - "EAQWA7VgpMnj3A5bH83MMn1p6WqpEUZoe3d54UsU5uXc": "GILLIONS", - "4SXJJw2GKVTeoxqjrcrFNb3S59632wXjVRSJN8kFpump": "GGK", - "GinNabffZL4fUj9Vactxha74GDAW8kDPGaHqMtMzps2f": "GINNAN", - "DmyYENoMKDFtbfUeBcMhAedi1amPSEvJC9Sy8R8VxQ8j": "GIRLE", - "B2Lfe3mjgszhieS9QbZHHDNcSdMcjigqp8GtJrc2pump": "GSC", - "9n1A8UtMXnnNdV1JJTc1sxhH47S4dVbcBgHqg96vdqQx": "PEARL", - "Fh7mLxtPAysdvHcMcJ37A3vc6WvBVh7JVDwxmwk6pump": "GBACK", - "3Xi3EhKjnKAk2KTChzybUSWcLW6eAgTHyotHH1U6sJE1": "GIV", - "2CsJHNyj74zK6Jaiq2UayPAXFEjmL5UKhpeLNaH45Fud": "GIZMO", - "6uYAMiY8KwDsW9ome5AvadsnNUKF19Evz4QxQ43Dpump": "GLDS", - "73rzMJ8uEqjAf5vE1YPGrXbg7Cb2Yv85EoxpJh2mpump": "GLAM", - "HJxLv6PnZJmLko7b5wqgCMyUT5fgz9NdBwe8htdMpump": "GLAZE", - "D12fZ1v9WK3VhpLGejb99GT6MzwPjGWLhjN8upQmpump": "GLEGLE", - "Bx6XZrN7pjbDA5wkiKagbbyHSr1jai45m8peSSmJpump": "GREMLINAI", - "2u1tszSeqZ3qBWF3uNGPFc8TzMk2tdiwknnRMWGWjGWH": "USDG", - "E4n5gooEoFaDPb1oSDmN2VXjWKekrTn3EFE7KjNKpump": "M2", - "X7j77hTmjZJbepkXXBcsEapM8qNgdfihkFj6CZ5ondo": "COPXON", - "XsybfiKkD4UmjkAGT2uR8X2sq9AWFtvGJM2KTffoALZ": "COPXX", - "EvzskrQ3vUUkiMGG1DzfSDyG6H2WCMy3v9G8fzzondo": "URAON", - "DsLQ18ooPjiHYuiuQ5Jz8PNCpVaKe3FhAYpvMxWondo": "PAVEON", - "FkBF9u1upwEMUPxnXjcydxxVSxgr8f3k1YXbz7G7bmtA": "GLORP", - "7wBBPnj2TA5XhB3ADm8D1odSKh9fDqAWMPaUqaMPH17e": "GLOWSOL", - "1VHpwkbT3TZ7VjqwrAxvPbQCg2HkBNMrPW2WsQspump": "GLP1", - "6DEa18xxCgx2SgBTJbFdEY6PTNZMWvG2nDv47LHspump": "GLUB", - "CiuH3kcRXJhdikbZsjfpvFaMaGVSCmFodWcwizjhhXcY": "WGLUE", - "mMykZc4tun2kFNgxbd7WiApAoGEaX8bxFuHjxzjnkrV": "GLUTEU", - "445ksMigJLms4mw29DQkXs8aWBG4S8Z2kTBCtGhd5nGd": "GLMPS", - "GMai1NvHreNe135cvxya1gHaUJZiT1NAvbJkF4hPHQJ9": "GM", - "8wXtPeU6557ETkp9WHFY1n1EcU6NxDvbAggHGsMYiHsB": "GME", - "A5LCTQ1vJECCQWSXJYs3rfCoexctbUgTCAEKDuNye8bZ": "GMEOW", - "9WoZqQfTmhU9VLzixaBhWnKA1QTySkSPpK3aortAHrP5": "GMICHI", - "Gcc7QfMgy9rKVdp8wdKqgUvzPWubHzDDfugiTgEvirt": "STATE", - "3acxNNmfdKKZj9i35P4VDBFm74Ufdt8ojKWceVGynwC5": "GM", - "3KzAE8dPyJRgZ36Eh81v7WPwi6dm7bDhdMb8EAus2RAf": "GOMINING", - "BaDjVCpABEVCdt4LT7ivuzA4izBwJCqnDjrLa8XBtT38": "GNME", - "2J5Dpp57RsjLBkJrEvyrAQpg8qWvgadUeJR4Ln7bpump": "GNOME", - "2D7jZLpUUAMboqzHMeyaxZfLWpf4JZUoR4i82Zva7sxU": "GNOME", - "GNHW5JetZmW85vAU35KyoDcYoSd3sNWtx5RPMTDJpump": "GOAT", - "GoATEDLwYeDQXyzcFxAgeiQd5Amu4mewU4KmbjvUWGvH": "GOATED", - "Goatm5cqggssKRUwbMnPhHXKtN5SDGEP57qjwTSHD1Xf": "AIAI", - "FKZWwdAxhAhf6kiKM5Jwk1vDjJqxxM3jdanexwS66LxM": "GOATSE", - "CzLSujWBLFsSjncfkh59rUFqvafWcY5tzedWJSuypump": "GOAT", - "DtWz93pDUZe5cYqBFmZjXq1wzZqZPygCeox5d3ajpump": "POPGOAT", - "CwdjgD54hgTQChspxHhirWLQWqy3EsxtndrcpLBqpump": "GOBI", - "3xypwTgs9nWgjc6nUBiHmMb36t2PwL3SwCZkEQvW8FTX": "GOB", - "4NtBkKssmv39VTrMJz212ZrXUWicJ1aepERpHnNCpump": "GOCHU", - "GodL6KZ9uuUoQwELggtVzQkKmU1LfqmDokPibPeDKkhF": "GODL", - "GEVqugYZESSzZaYU6SKdpW6znCCEtH7aoSTzPHbqpump": "GODS", - "6CNkQVWLSRxZEa1eZ2PFjQyLCVZ48U9oGcggdS9cTJoK": "GFY", - "E1jCTXdkMRoawoWoqfbhiNkkLbxcSHPssMo36U84pump": "GFM", - "Byb1W95xDYBiF6gMJoTRgpD7Z14GFna2QLHoZfEWNbfQ": "GOGO", - "2Wu1g2ft7qZHfTpfzP3wLdfPeV1is4EwQ3CXBfRYAciD": "GOHOME", - "9sjyR4GrozeV8a9xM3ykKPGPXJYASy9AuufzefCyaCnP": "GOL", - "DK9nBUMfdu4XprPRWeh8f6KnQiGWD8Z4xz3yzs9gpump": "GOLD", - "GoLDppdjB1vDTPSGxyMJFqdnj134yH6Prg9eqsGDiw6A": "GOLD", - "79eXoT1aR7qhBMGKXzVNdV4ZcLqHsupqj2WTFvDumpSZ": "GOLD", - "H1PevUqmTe8WU9An9JCxt2LMR4WvesNjMF5R7GZ7qpRE": "$GOLD", - "Eh36AX4m5CwyCHHcAy95oWRop2EMYVJbLQq7q77hsX9N": "XAU", - "9QFfgxdSqH5zT7j6rZb1y6SZhw2aFtcQu2r6BuYpump": "GOLDCOIN", - "H2eWtG57do5krGxpZdzs6sDddHLz5Nny7797YhR4pump": "GDIG", - "GjqpHLGPUtmBbfGvEAEvVG6GJkfyAozNZb1uJLoppump": "GOLDEN", - "HREdVBmGvUvdgvoGeHwYpEQNJRb1oqmScwV5z1dHpump": "GAPE", - "7dGbPgUxKpB5qWiLRKcTQSC3om1fPzUpgGAFfwej9hXx": "GCR", - "n88wHcVtfavStAM5VQyUFQ6ykdnfqZucysVgmqpdgiM": "GKAPPA", - "ER8HgvjkEa4c3ToUTGZXAcNH3Vs3XKu3KGcncM8jeJUQ": "GOLDINU", - "BchJRy2snmhJZf3rQ9LJ3ePs2BGfYgfvQNo31d2ondo": "GSON", - "XsgaUyp4jd1fNBCxgtTKkW64xnnhQcvgaxzsbAq5ZD1": "GSX", - "AEv6xLECJ2KKmwFGX85mHb9S2c2BQE7dqE5midyrXHBb": "GLDR", - "Xsv9hRk1z5ystj9MhnA7Lq4vjSsLwzL2nxrwmwtD3re": "GLDX", - "Dq3Fwkvo9zC87RmZ6ZKEYiztnAyr6qX9wmYjHJgiBAGS": "GOLEM", - "7L11EwmNRz4A4oj6jErkFR2CjSbAPadgwAGz7vGobonk": "GOLIATH", - "Bx74hpFiaiBbSonrjyqxjGfAA7gRxM2CeKiy31uN6biR": "GOMU", - "DG3LB7Kr8Londuz1j2us4YUXyNqiiLioKdYjSB37HbN2": "GONDOLA", - "G9b9Lt78x5JXhFLeDY4ZAfMkWWugnbsfKBR2qipJWXaT": "GOOBY", - "e8NFqLCMJBfDTUUARoniADgQtSEFmZNBksPxYzepump": "GOOCH COIN", - "4DNcAxMMhWffAy5WTiJyxoCnMMkeXzrcw2RXJrakr7ym": "GOODBOY", - "goodX4LG92UAcRdFRUykfP2fAfzQAVttrToEPxtxSkp": "GOOD", - "GGUSDyBUPFg5RrgWwqEqhXoha85iYGs6cL57SyK4G2Y7": "GGUSD", - "4aL5GLRuzsnJjJWNdXK7TPTVpGhP6PyV4ZhyQiyxpump": "GM", - "9RaBDMwL2hcmah5yzXjBLiiYxHiBbd2jSEH3q6KCpump": "GM", - "FT8X8hqoEwNT8UifNL3ppPbpccf7mnuNLpJsszv7AFHW": "GOODRUDI", - "CVNb8Bg7wCrtNTFniaJvxjyFW2cKcoRjKt71Af67pump": "GOOFY", - "8cyS5xMeWSCAKSe5QpQ5XMfvxRCcSj3mbHtbtyrqk7RA": "GOON", - "ENfpbQUM5xAnNP8ecyEQGFJ6KwbuPjMwv7ZjR29cDuAb": "GOONC", - "EXz2e3qNMkTnwVhmYusAP9VtSXFMQ1eAmLS5treobonk": "GOON", - "9dUKXKC5K6rWErKXbvCDhvnRaHR3zoFZYBqRLdFQpump": "GOONCOIN", - "FP9axFNBC1V68BLTDqtWAP6vVVqP9daNr57wmx4Tpump": "GOONER", - "44sVpouhWHXzADVsVUo863tvgmaw6LNQo25fYRs1pump": "GF", - "B3seNmLjE4sv7W6HCjfzcKFnAWTfHDv9MBa43zEKpump": "GOOSEPUMPS", - "71Jvq4Epe2FCJ7JFSF7jLXdNk1Wy4Bhqd9iL6bEFELvg": "GOR", - "8Q8KPBL21FVatn2C1EaxAQSorAkHW2W2jDUXapVPZmhm": "OSCAR", - "UbESBaztbkxJRWxPcfDeK8Fft15igTbrv3sed1bsegM": "GCATS", - "3XFXWiPcVee69nFCRs7G3eZn67TrXuuLmCYCo8d7bonk": "GROF", - "76EDL5qNcte2ZH29quTaFzagXjWAEY83CATEjyA6pump": "GORK", - "gorkrgj6k4K449Qa4eF1NaWC5cNLQtMsVEikhpGn7fC": "GORK", - "9Z1Npnb23ynnZa6ZzzwkGNYm9rmTuFmm6ePFVuLNdaos": "G16Z", - "AAqZ6CECabwnYLm3FDqPrVQotXLPGQ3BJSfecYmXeJU5": "GOTM", - "FoAnSCG6CcqTq2rsTi58yyYBNk1HgsbLzS6b1kTP2ACL": "GOTTI", - "GRQfnwWyhd4qeJqUVCZo9ku61p3YN7MWCCk8vBXnpump": "RICH", - "6AjB9gafah5aBBsogmxmLyeQkrRDejKzxETxJzRRKF1H": "GPUINU", - "B8CTmq5ghNJg4ZFKq3rKLMEnZBotEwhrr1a9Bx4mpump": "GRAB", - "m9GcsVgdjaL3KsdtSFHimnhtsUMpTHkjtwEG4Tzondo": "GRABON", - "7SYuU1Z6EKfpYQQ9VeTXfLW5ofhigdn8Y7iQsqoepump": "FEROCITER", - "5EKFigDd1fhbGxq8hVTzBMcGhJZ1rKxL5aZtPuYqpump": "GRAMPS", - "8upjSpvjcdpuzhfR1zriwg5NXkwDruejqNE9WNbPRtyA": "GRAPE", - "31k88G5Mq7ptbRDf3AM13HAq6wRQHXHikR8hik7wPygk": "GP", - "Grass7B4RdKfBCjTKgSqnXkqjwiGvQyFbuSCUJr3XXjs": "GRASS", - "GkeUTuaFB4oZADaqiCo3yauHNx9SxUNn8euHptEEbonk": "GRASSITO", - "3hrGWXzgQzAikcG6rerG45tK7KLYw2dZ84owWJ14bonk": "GREEN", - "49STvRfTDg1AbAnvYeB8to3Hj3E9UQb5TZp3wXstdaos": "GCI", - "5drB44YYwNVE6RyxZyRRLbDyEAHgdhkJVgsgc7P6pump": "NORDO", - "63Z3Q7JX3SBGDiiwqqnPTVvHcuUk6ixkzsYQbKzhpump": "SHARK", - "AFbX8oGjGpmVFywbVouvhQSRmiW2aR1mohfahi4Y2AdB": "GST-SOL", - "zZRRHGndBuUsbn4VM47RuagdYt57hBbskQ2Ba6K5775": "GREG", - "X69GKB2fLN8tSUxNTMneGAQw79qDw9KcPQp3RoAk9cf": "$GREMLY", - "BtDFyQ1mjL4RHiXrx4ARuzXUt196u1hWoF2rgWUHpump": "GH", - "A21r5h19iHhQt6b7vzkgHNgMaSAR9YSazGeJedJepump": "GRIDDY", - "DWGxNqh3oQYfZostoCXXdMRaB67WCDYckUjYvsMopump": "GRID", - "KENJSUYLASHUMfHyy5o4Hp2FdNqZg1AsUPhfH2kYvEP": "GRIFFAIN", - "7h5AzpYTAnh4Gyux8Gqko5Fvx4AYQBZdkzHZ2CsBudvJ": "CITADAIL", - "Gc1aT3ay7FXL3qdAW7cNSXYPDsGavy7qiACuxwxondo": "GRNDON", - "E8iWdPoeVDHJn1EHifeCAFWvKZyTN9WXrmccFEx1Hxa": "GRIX", - "BQ3F72yt9FVRgYrqCVCG3YohyBesDZ9bTuhGdmQ7GNEF": "GROK", - "Dp8fc49A73C1ycnTzRet8oEWNRSpUymqcCbYsse5pump": "GROKAN", - "WAxxkziGPy6ThGBLg4MNKJce4eGrSKxCEfJ72ZLpump": "GROKCHAIN", - "67ezHLk8PUkjJCXjmmgPbx85VowA52ghfRXa9A8Tpump": "GROKIUS", - "AGBegePeNtnBy1xLGBDcBDjxaVHaDhvebSNMduLapump": "SOLANUM", - "Cc3A1tC5uoM6vhj6oGiY5hywHMsairxij1siKL6fu45S": "GROKROOMS", - "7Y6Rix8X2botuMaJ2mno4UqSWdQZqU4RezY6qp7zpump": "BOLT", - "BTyjf4y7sLXgtahZCH6X7gGRnGgYqRFyBSDjL6u6pump": "GROUP7", - "12vYRvkvnZnY9e3J2mzZgfuE5u5e134tqHCeMcMrpump": "EARS", - "AjgSvYmJLhvt3FteiyTqQf8XBj1SVs6T6AmSUfkHpump": "GROYPER", - "DUhWgHD3KgHHmsYdQdJHDv359bySNzigYcqJ45Gcpump": "GRRR", - "7LtgzCmSpGocBajm84WghiWbu1NcvQxdqLQtUXFSXgvU": "GRUG", - "BV2CATw6dAcxxAoZ73P14DTXzbKTUZR41X7EXpKWpump": "GRUG", - "GRUmPYbiTpq9ZPy5LAqBMMze7kErf5dEX2i9qYfwoSmR": "GRUMPY", - "AZsHEMXd36Bj1EMNXhowJajpUXzrKcK57wW4ZGXVa7yR": "GUAC", - "APmLv2VarkC5yEjjutYkeK3byr9L2KwCgrVMWHWsgaG7": "GUANO", - "A2X6xEhZUCPiHstrGehbNT9TrdEh9hEDfyYfNeNuAdDy": "GUARDIAN", - "GoDCaGctsLDnkV8T6jBAfTeMj8DesxT71CkZekV5ZXcf": "GODEX", - "5QUgMieD3YQr9sEZjMAHKs1cKJiEhnvRNZatvzvcbonk": "GUDTEK", - "H7bTHGb5Cvo5fGe5jBDNDPUv8KykQnzyZA3qZ8sH7yxw": "GUMMY", - "3jUf2RTyXp867piSB2dt8uUcNiLDW58asjGtXkRAkBbe": "GUN", - "42d245pXpJDhAkX5QKTcLxPB6QxET6KbbsDas71Tpump": "GUS", - "3FsEyF1yXogn2zYZK3naeyCymSLgkEv1kq2AhLffpump": "GUT", - "CY2k3sedfk8x3ejAYFcYp4krzoPvpxk6y8ejRywUbonk": "ANON", - "EfgEGG9PxLhyk1wqtqgGnwgfVC7JYic3vC9BCWLvpump": "GYAT", - "Crn4x1Y2HUKko7ox2EZMT6N2t2ZyH7eKtwkBGVnhEq1g": "GYEN", - "9SUCZg1RMpyMMYzRP9Z3JSZFghXGoXerEzjKQvbSpump": "H0L0", - "FNqJtYs7rsP1H9GXWTtc5VnDoL2GhXEUKhYN46EEpump": "H2W6GM6JZ", - "864YJRb3JAVARC4FNuDtPKFxdEsYRbB39Nwxkzudxy46": "HABIBI", - "2Bwbeh361ywi1PpypDHabDb8VphNKCQt9ht2CRzFsYAE": "HABITAT", - "x95HN3DWvbfCBtTjGm587z8suK3ec6cwQwgZNLbWKyp": "HACHI", - "4amstKcbziHCqwev9esMtRGDTdjHSviiNXT7WtajgjUq": "HACHI", - "6bzBfcyqmf8SqvtGN4SoLsT3knNhNycseuyZzKzjQnSa": "HACHI", - "BWXrrYFhT7bMHmNBFoQFWdsSgA3yXoAnMhDK6Fn1eSEn": "HADES", - "7sGdNQSvUGpahh6qyXB3g5gsdK9FAzZM299KyCXspump": "HTTPS", - "AYyYgh3i43s1QSpvG4vwhJ6s3gewfN7uteFwYrswgMGw": "HAGGORD", - "rizo34MUwbCBqpSTSfnEktdWB4CTByqqYh8zBxL3WAR": "RIZO", - "haioZJAWTMaR4SFhZRwFBBejnGDPiwLTqrDiKpwH31h": "HAIO", - "6uQQGTmjEDKP8Ag1ST3UoXkLjjzuU4Ax7mYcnu9Tpump": "CREATURE", - "EjamcKN1PixSzm3GiFgUaqCFXBMy3F51JKmbUqNF99S": "哈基米", - "J9SXKctbZVws65vjBHL91mqStzq5an1xZxdi6a4h853o": "HODL", - "CvTMSEB9RzHPhB1CgE7PAkszomozDv3JJNz28sDbpump": "HAL", - "4sp2EUDrQf46rZun6sYAWzjrXwUpx2T3njuoKmV766RJ": "$HAMI", - "6xwmWkBheMwGLyuQjPN4WeHoK3QTGmWQtuknbFCSpump": "HAMMER", - "GLXkHLX4jiVRAYBVwx2UAvKRBu2YDCQudNiR3k9q9THH": "WIF", - "FY9HAYXgQu36xQDsaTLzwVHiR3HTqVrAGAq5ArLmpump": "HAMZ", - "GTFWEVQy5BwQsZJWS4Y6KaZ3or6Yhysh2EEUp8bgpump": "HANBAO", - "LkW6Lk37bdvX4j3pmwRKDEu8LgZuBuRY1V8cgRyESrR": "HAND", - "3AvXA85wtW4UdAk1mnJsncGKvfEngsTypC9nybZJbonk": "HANDGUY", - "231Qq1ZzBRXeSmwa1HJCFrm7v5cNUhyYuz8va42vtdQY": "HANK", - "6VNKqgz9hk7zRShTFdg5AnkfKwZUcojzwAkzxSH3bnUm": "HAPI", - "aBvs3Zv9JYmvUCKqRJvGctDQSCt6R7NAMELid3FeqsQ": "HAPPI", - "HAPPYwgFcjEJDzRtfWE6tiHE9zGdzpNky2FvjPHsvvGZ": "HAPPY", - "4TNLqKKQrMDqveiyKQjwX5QUuDrgJdzQC53tX68rpump": "HAPPYDOG", - "Fch1oixTPri8zxBnmdCEADoJW2toyFHxqDZacQkwdvSP": "HARAMBE", - "CNL2opdqaQDY7yudm5V4MAaTwuuY32LTKqNP93jZM8X6": "HARAMBE", - "463vBSJt4CurDqEnojG3ykMy3Lf9GQF2hFV27JeZpump": "HARLOD", - "EjmDTt8G3T725eFSV7oWmGD8J848guo3LZ1EB3RfwGSw": "HAROLD", - "3vgopg7xm3EWkXfxmWPUpcf7g939hecfqg18sLuXDzVt": "HAROLD", - "7oZCgJNtCFvBNBNx7S1Nza9TwfzSNaovXMkfnk4gpump": "HARRY", - "vv96jGJhGEqq5GU6q6zqpPrGMayZpGxQ35rrGEkpump": "MILLENNIAL", - "CTgiaZUK12kCcB8sosn4Nt2NZtzLgtPqDwyQyr2syATC": "BITCOIN", - "4acvcTQhR2Z3TWauJZHUFaubyyEBtoybrp5QLobuCkcH": "SOLANA", - "zSyuawLybV374Fiufx4bQnLnwfFRxX2cFsQ6kwKpump": "HASBULL", - "7ZqzGzTNg5tjK1CHTBdGFHyKjBtXdfvAobuGgdt4pump": "BARSIK", - "sh3nQze56RGQFBxr3FZidbbVNVh2oUctgt2sGP4pump": "HNTR", - "7LWanZteUKtvFjv4MHYgKXXdAuCQYFPJysL9pxxdRQGn": "USYC", - "3b8X44fLF9ooXaUm3hhSgjpmVs6rZZ3pPoGnGahc3Uu7": "PRIME", - "8fr7WGTVFszfyNWRMXj6fRjZZAnDwmXwEpCrtzmUkdih": "WYLDS", - "HrqgFZipMFHXvN5nKvTUaCwuA3Tp2UGqcQzArRGAyQ22": "HAT", - "BKipkearSqAUdNKa1WDstvcMjoPsSKBuNyvKDQDDu9WE": "HAWK", - "4GFe6MBDorSy5bLbiUMrgETr6pZcjyfxMDm5ehSgpump": "HAWKTUAH", - "GJQpf6Zjvokd3YK5EprXqZUah9jxkn8aG4pTeWL7Gkju": "OKI", - "5t3hBZDnxksnWiUu1jw42rq4b7h3VCSXUSTEojHDpump": "VIVEK", - "97sraAGszzEFY6AW8iuWfTceBQWa1RzuFRXryJqSbonk": "HEARING", - "Gv8mfuosgCKCj9tPs9VWS1GzETemr69Lnr2jwjbWpump": "HSM", - "LiGHtkg3uTa9836RaNkKLLriqTNRcMdRAhqjGWNv777": "LIGHT", - "htoHLBJV1err8xP5oxyQdV2PLQhtVjxLXpKB7FsgJQD": "HTO", - "8G5ayEsJF4Q7FEWEGeF4jtnUWZBEKCqhySTFQf9Ppump": "HEAVYPULP", - "A5GeMaHdM281F5GkwA8GnToKZUPPubWuMB99UD4pBAGS": "HEDGE", - "FFYLoYzPnD1CAVa8UeP9BeXhPPAykRkYiFLqVpxKbonk": "HEDGE", - "ER4NDfcEckUX5PzqtoLDwtAbCYhhtccCABXCLH4rpump": "HEDGE", - "9dLuVbJMd4ZpTpFgmaFHAGSsFwVjtcnzFWaLAA1expHg": "HEEHEE", - "ULwSJmmpxmnRfpu6BjnK6rprKXqD5jXUmPpS1FxHXFy": "HEGE", - "BreuhVohXX5fv6q41uyb3sojtAuGoGaiAhKBMtcrpump": "HEHE", - "8Q9VDdrd1rNczTEL3ivPhwsYrYXVuSAdHuY7gFJGpump": "HEHEH", - "DdyoGjgQVT8UV8o7DoyVrBt5AfjrdZr32cfBMvbbPNHM": "HEIDRUN", - "E5vSaRkSUDe3ob3KTAzXa6gmCndknzutn4hNtf2Qmoon": "HELIO", - "hntyVP6YFm1Hg25TN9WGLqM12b8TQmcknKrdu1oxWux": "HNT", - "iotEVVZLEywoTn1QdwNPddxPWszn3zFhEot3MfL9fns": "IOT", - "mb1eu7TzEc71KxDpsmsKoucSSuuoGLv1drys1oP2jh6": "MOBILE", - "XsicTFC7G68d74FaYDctWggkKDgkMx8LpNoFNr81TrF": "HSDTX", - "he1iusmfkpAdwvxLNGV8Y1iSbj4rUy6yMhEA3fotn9A": "HSOL", - "9YdvrMsW3qsfa2sRUEgsdMtC4wswGHNbDNWFg4wrpump": "CRAIG", - "4h49hPGphLNJNDRyiBwzvKoasR3rw1WJCEv19PhUbSS4": "HELLO", - "YrJEAT1TyQfDvaY1SmBWWHLAethSp8bbQjoRJccpump": "HELLYEAH", - "BriHnFv9qa8Hrzjgh4vmvp1WxzECPUTyZ4VHbRe4BAGS": "HELPFUL", - "34AeqX5MQGyrey2VnZdkeZCfM4qinp4F3X7pGxtipump": "CANCER", - "819T1na5X5PwCT2WP6ahrjoAhZuiQXKZTs4zuquQpump": "HERMETO", - "FBhF2pcytFSTcTzQWCxisLCyrYDqmRZDu4mrGp1Spump": "HERMY", - "64GhPSS8P8wNaGWH2uysASxp9XYsqS3An3eJ3w5YNJK9": "HERO", - "5bx4ttYasp3nRe51vytPgkdsTNveby2zTWTsd89epump": "HPDA", - "WXRPpJt44kNmiQK495uuxPkDPHa4js4K1cyDEWXCNKY": "WXRP", - "9McvH6w97oewLmPxqQEoHUAv3u5iYMyQ9AeZZhguYf1T": "ANON", - "HEYKwDJRoSsiLNQ9F9bZFKo7yxum1WbTFCgiHTX5h6tb": "$HEY", - "FHKiJEg2zmhv9DEeaXMSZa7R4P8BPFX5VTRrzrtJpump": "HIBER", - "BDW8YHasD3NSDjSHU9Xy6KXtshGayMGQfj5bJpLcpump": "HTERM", - "teBPKvR8PH4n58BCvHKVotLKaV9RAoB8taWTmkMpump": "ATH", - "F7BW1wJsDHrHUkNPcoYkgYcKD1o2DsUooML9kjajpump": "HIGHKEY", - "Hnq9gGF2HFShcNg7U6pU1xxNs7zwvD5E1LoLdjMApump": "HIKI", - "bdh3njeo19d2TBLAKTGvCWdSoArfVw8uZBAJHY4ondo": "HIMSON", - "6GYrrhBy3s6LPgLdrvfd8i6HvhsgmQDEFP7zmNvZyBLV": "HINT", - "5tZHKgJpnF1hsjSnQ5N9NA2c3S4QzaFw3uq2ARGspump": "GORT", - "FJpuy1NFuAb4uqkBVYWzsc7gBC9iDj3QT5yaDSZdpump": "DONCHO", - "89q6aHpZ1fXhuwpnrBgqmCvuAX4GaCrRPQNp5xVHpump": "CONCHO", - "HiTD4kFXbMHcEuqsr8XYEUR9FMeVAJWKqYWCNehiYQbL": "HIT", - "6fnYdoJhYkifvt52pfNtUDr31ZYXmof7JiL9SFrMpump": "HMU", - "FeKeCwKU8gaULTGeLuJN5jQg1f6SiG1aeUSZcG6Rpump": "HIVE", - "9DHe3pycTuymFk4H4bbPoAJ4hQrr2kaLDF6J6aAKpump": "BUZZ", - "HGTxJQKzuMjzKkHVpRLcFHwE6ngVCJbcP35FDQtmRzhx": "HGT", - "Gdck9KXSSiMMhNyjUjo4sVT1GDzeZnZP2yse9jhax3GR": "HVLO", - "4vMsoUT2BWatFweudnQM1xedRLfJgJ7hswhcpz4xgBTy": "HONEY", - "7xywcaBrF6vQVyHJ1eM9LdT5Z45EAbLbDgGuh8i5pump": "HMMM", - "58UC31xFjDJhv1NnBF73mtxcsxN92SWjhYRzbfmvDREJ": "HODL", - "EpXBP1mNofE6j55wAty4poUh4cJPQS7NWDdCD9HSpump": "HODL", - "8N9cKkE93grPT2ALLTQ992wwhTNXoqycsCg9Jj5wHiyW": "BRUVS", - "84NdpWVhTGVuPFximNBXHQyQQPKumTjBYfFYGqt4bonk": "HODL", - "3WhE7MufQUsQWv56yaRtjd8N23sRBV1VT7cmce7mpump": "HODL", - "14TGkkYQQeNj5pAKKZf8VGJQJfDagdX1t4QX4Rc3bonk": "HODL", - "12Z5CsL5kCPRmnLQcwpY2QrEub3MahHCtRHxoQEdpump": "HODLESS", - "13qAYdft265v36MFbFKSANKo2whvGb6s3ZhpJqQWsump": "HOG", - "74gUPK636nrieDT9kLSgF1peWg5BB7itNkjF2BkMDwZj": "HOGE", - "8CuPzHKuZQUyYbear8fi3u6iuTY652WVZT2cHZ2yeNbE": "EARN", - "BS7HxRitaY5ipGfbek1nmatWLbaS9yoWRSEQzCb3pump": "DOGGY", - "3DH8WZ6C2HdDR4J9ekYnTfai8Tx8mxn92mW8zcYmP4px": "HM", - "HxyAjioHtx3xL3ytxAMJg3AgWWXghXx2zgZFk59pump": "HOLI", - "48TqCgU8zC2H5tWshNriY2bWHDULSTSvdgL4iP1Fpump": "HOLO", - "8HWLLoctYa7aFttfpHP9zbcEgV9uB9MUoVvSLjhBpump": "HOLO", - "69RX85eQoEsnZvXGmLNjYcWgVkp9r2JjahVm99KbJETU": "HOLO", - "B8hCuoikV9gLeuwmTyhNdLbPnb5k3P77Q7WTtEM7pump": "HOLO", - "BSXFKLVsyrYckSF4SGXu9DRfMb3Fos7tRF9zMXgRCUsP": "HOLY", - "8uHzYxskWmU8HcXvpTJCjNtnWpLhP6vMEB6Cu98ypump": "SHITCOIN", - "J3umBWqhSjd13sag1E1aUojViWvPYA5dFNyqpKuX3WXj": "HOME", - "2hXQn7nJbh2XFTxvtyKb5mKfnScuoiC1Sm8rnWydpump": "BREW", - "MtEXKVN3Pcggy8MPA3eJr15H6SK3RXheScqj9qtondo": "HDON", - "XszjVtyhowGjSC5odCqBpW1CtXXwXjYokymrk7fGKD3": "HDX", - "DxiAg5mqccrmYJuG8JA2RBhYrPvokoU7wPqc7ZQKpump": "HOMELESS", - "7Uuzh9JwqF8z3u6MWpQuQJbpD1u46xPDY6PGjwfwTh4o": "HOMO", - "hnstrzJNEeY2QoyD5D6T48kw2xYmYHwVgT61Hm5BahJ": "HNST", - "3dgCCb15HMQSA4Pn3Tfii5vRk7aRqTH95LJjxzsG2Mug": "HXD", - "XsRbLZthfABAPAfumWNEJhPyiKDW6TvDVeAeW7oKqA2": "HONX", - "3ag1Mj9AKz9FAkCQ6gAEhpLSX8B2pUbPdkb9iBsDLZNB": "HONK", - "3EZKbpU2JDQi1XUcNujqgKPG3u4yJ9CvJcaKNRbab85P": "HOOD", - "FFButxhra5PbGajLN3e1LhVWtTp2BtUoWoJJGSVBpump": "HOOD", - "CnbALMqQdGKtcYrBk6f9DqMh8oCcsp6BvtXta8DVpump": "HOODED", - "GNYLexaKyy7GHX8wiKVCmohNcuh6fvtrFkPsbVypump": "HOODRAT", - "58ExX8TFqiDqUpr1ZGiAhhiUsKfT14gwTFykLoVCpump": "HOODRAT", - "HootvhN1jGXQk8APL2fD1StHqu9eWZJbMmfdutf9hnLV": "HOOT", - "ECZxKmKGEkyKhYUau7WkUE1L9Jp2yLebwX4SnKc1pump": "HOP", - "2JVZ7LF7dPj8EfmKcGe6CPjXZRLRZqe3wE66jR4Bpump": "HOPE", - "3g3YFfUZLspiKpVmv9mDkaDcKh168CLArcqShR64pump": "HOPECOIN", - "7kJejVVe6wRj1RiLTjZM54dNxTY1DPV6iE89VzN7pump": "HOPE", - "8AS9yeGsAwvTs9gCDKMmB2MgX8NiSvv4uppH61yqpump": "$HORNY", - "7S2bVZJYAYQwN6iwwf2fMMWu15ajLveh2QDYhtJ3pump": "HORNY", - "GJU3bXxNkNtYxgjkoFhAdq7VrXJAB2GW4Cpt6kLcbonk": "69420", - "yFtG6tU4mZqiUnrBC4gF9eXTdWyzj7ZSKb92wv3bonk": "JAIL", - "E5UdugdLvgsccqe3pdq1ivNBTZhaxwthjvnE85Kzpump": "HORSE", - "EqquikmAsy62SHadHzHnVXWusLRnWtP2vgseAthdpump": "HORSE", - "Dx2bQe2UPv4k3BmcW8G2KhaL5oKsxduM5XxLSV3Sbonk": "HOSICO", - "7bdPWWy7qbVZgjzR5fr5dQK3nohZ96HB5Ey8Jyaypump": "HOTDOGE", - "H4SFaUnxrZRoFnhBeotZnuqw4mfVtJ2nCvGrPmQupump": "HOTMOM", - "App2Sp9pgmQG7yD6uVaygULxALf4TpfALgnhHEkJimih": "LOCK", - "DitHyRMQiSDhn5cnKMJV2CDDt6sVct96YrECiM49pump": "HOUSE", - "BR4sacH5vBdoJsdV7aC3X4JZTAAtBPFsDEQsGLy3pump": "HOLD", - "2Pah9ZUfpii27ABrWfQorzdW8FSJimxj6izFr4oGpump": "PLUMBER", - "77RBCP95AFT9XRsx4xuGUHjBQsjcatGYCZ2VXx8Epump": "HUAHUA", - "HBB111SCo9jkCejsZfz8Ec8nH7T6THF8KEKSnvwT6XK6": "HBB", - "B8zW7B8T7ntCiiRYw18jrFu9MBqMZVk9pP7nYyT5iBLV": "HUCH", - "96zMCM9nALK2cDv5p2CiDkGrgbJX9McCnQkxyBaoBAGS": "HUGGI", - "6Ra49aqZTbEurJB1UQgAj1TjvbqajGP5um7gsTym8tWm": "HUHCAT", - "BTWvZsjwgnGT7qSM3AAXxjZohCLsMHpdAhaiQMJbBvcF": "HULVIN", - "HUMA1821qVDKta3u2ovmfDQeW2fSQouSKE8fkF44wvGw": "HUMA", - "HUMANx3MBnTtMqZ9PyxnrtjpiGV1S8sncFziWbtUejk9": "HUMAN", - "WETZjtprkDMCcUxPi9PfWnowMRZkiGGHDb9rABuRZ2U": "WET", - "2XPqoKfJitk8YcMDGBKy7CMzRRyF2X9PniZeCykDUZev": "HUND", - "HUNT4B1dJWqTm7aZCqjUW6L3KAcEoZwqedf9eknLKWrx": "HUNT", - "HLwEJQVzs7SvjXuXpBTRHaLp5S6uWoWTUhLjJxBfy1c7": "LAPTOP", - "22513u2QwiY6xaJn7nVFWGKy3aBdw6WfZsRPW2RRtCKj": "HUNTBODEN", - "7dVH61ChzgmN9BwG4PkzwRP8PbYwPJ7ZPNF2vamKT2H8": "HBTC", - "Ez81MaRRsT8WSc4w1kmxZh9vSnyY5QySSoHy4yLrpump": "HUSBANT", - "HrTofxQLFCF86icrLQ81KRLsYM2iWVZEsKtunwVz7qNe": "$HUSKY", - "XsG5QyZTQnVSpsXRpD92K5ZGoxXjsmfTyx7fW7r18DV": "HUTX", - "HxhWkVpk5NS4Ltg5nij2G671CKXFRKPK8vy271Ub4uEK": "HXRO", - "7XiycwkFwd1ig3yhncPxaq3qwJ6S2RpHc63C541hgmdC": "HYDAO", - "4sWNB8zGWHkh6UnmwiEtzNxL4XrN7uK9tosbESbJFfVs": "XSOL", - "hy1oXYgrBW6PVcJ4s6s2FKavRdwgWTXdfE69AxT7kPT": "HYLOSOL", - "5YMkXAYccHSGnHn9nob9xEvv6Pvka9DZWH7nTbotTu9E": "HYUSD", - "9iQFnxrDDMFrhLx2pYJCDeqN3wFuaBimQkUnZQHNpump": "COOL", - "FEiphCSAqjAxvtyMxj8boFTABrZBFyngfyrZueqeMw3q": "HYPR∞", - "57iaCFGwsfZYCurbrgmtrDnHggGP6B6oHSdWD4nNXo9T": "HYPT", - "8vBMibwpn8wpfYKbQ9xqzodymg3LjmYec2tSNGRy23K8": "HYPER", - "92s9qna3djkMncZzkacyNQ38UKnNXZFh4Jgqe3Cmpump": "HYPER", - "Aq8Gocyvyyi8xk5EYxd6viUfVmVvs9T9R6mZFzZFpump": "$HYPER", - "GwkEDwePTa6aFosh9xzAniGK1zvLrQ5yPJfLnqwmuyhG": "HYPERSKIDS", - "7sYAhEucFNkme29jHqSeFYNenAgD8krH9KmJeUs8Un43": "HYLX", - "BUDXpnu7opuGuJkQZdet9nAxSzb67MLJpkzJmaKkpump": "HYPU", - "76RZeKinKT3jRx7aQZsV8TuDfB31f9HAQLwqp4Vcpump": "WO", - "5w68xqRpVFqi7hCXuN7QLc5ReLv1PfpmT9TVm11Cpump": "HIM", - "6ZUQAjQsAr3JQRRmbf5wXVgpCxD8vuq6jZGfRpTspump": "APED", - "GUy9Tu8YtvvHoL3DcXLJxXvEN8PqEus6mWQUEchcbonk": "BOSS", - "AhiEuwGafsTbu2oUkk1Tmbprg1wdUvWiUCLVvAwvBAGS": "BOSS", - "C8bZkgSxXkyT1RgxByp2teJ24hgimPLoyEYoNa9ondo": "IBMON", - "c5ug15fwZRfQhhVa6LHscFY33ebVDHcVCezYpj7ondo": "HYGON", - "9VbsZNJvcyyZ9LKo42Yyw4h5p9KZgtZ1BoTWFoPGpump": "ICE", - "G3q2zUkuxDCXMnhdBPujjPHPw9UTMDbXqzcc2UHM3jiy": "NICK", - "G5bStqnKXv11fmPvMaagUbZi86BGnpf9zZtyPQtAdaos": "ICM", - "BjcRmwm8e25RgjkyaFE56fc7bxRgGPw96JUkXRJFEroT": "IDLE", - "CymqTrLSVZ97v87Z4W3dkF4ipZE1kYyeasmN2VckUL4J": "IDK", - "idrxZcP8xiKkYk6XGD4uz1dxEYCWSgKDHqgjsBbwDur": "IDRX", - "RqGHXg2eh3zPjyzNM7kUyrrSmhb9M9q4mCKFH1mpump": "IFSCI", - "9CMf9Awr12juc8oSv4XrvZUwXsW4Jhaakm5FT53gpump": "BUYTHIS", - "GJLiErro8cbWeDngDMWJug9dkwwckYZg4Lvb79F3pump": "ILY", - "Be1FLsZBPPkDi5yjtS9g9rLwPtWua4hrGuC9QyPNpump": "IKA", - "2ggn9cSrL3HyUudaNbB8PzjPkK5WEidK2MyapbnMBgA5": "IKIGAI", - "EzEGH66Xheme3C4LKeV3V22axMnPYFAbhBcXwh6JybC5": "BALL", - "AixSWr1ZqjerSLuqzUPNebu3y7xckzmQanZVcCfwX5gt": "IKON", - "AtortPA9SVbkKmdzu5zg4jxgkR4howvPshorA9jYbonk": "IKUN", - "BUZFom1YPnAZVYSccbpddBmFYmHcS4Xtc3JQG5cFpump": "SOOTCASE", - "5zCETicUCJqJ5Z3wbfFPZqtSpHPYqnggs1wX7ZRpump": "SPARK", - "BzggRTH4TQb8x3MeensGffz7NUa6wWCETcDA79Ptbonk": "RETARD", - "8Zk5n3pZotY83sZCMJ774HTyAfwpjueLHD3MrzoKpump": "KAIA", - "fnDj6iuSBBruq1GX5GQwxGa3MvAxiJbnPBSvHt4pump": "ILYSM", - "aPig1huDbdDTR9HmJBSHK5jiHrG3o2ZThdG4xPDpump": "SOURCE", - "EZGB14NYbqMrsw1QKSgfLXKpj8PrnQBXSjWHbwxpvbAy": "IMAGE", - "EuU9Vh37eKDdUnZkUfMZLGCcBqwFYpr1E6X3ZwUxhvgm": "IMAGINE", - "3Gjckk5jXnJffBruUS2EEYhpiDEN6z5TPXLkFVHkSkkg": "IMARO", - "D1hmg8DP6qP514Lxpy85kMNXKJhg7Cm8KkHM2Q7rpump": "IMG", - "7ctK21VZcJX1t1jiWLyWkSJk2Wm7xHYdsnWegzR6pump": "WOLF", - "5a5iV4jTaxyPP4mMeeRfZStz6tYQ65p2CKTNygdRpump": "EARLY", - "8z5VhoM97qYGZM4UH9VH2BWhKb6Xyf7kB3J55hM4JUNF": "IMO", - "8Q194jXJLBdr7Dp5UYnHiv6ThELp33ZGKRyzpt5kjups": "IMPULSE", - "EjzzyCSiLqjFDprpZj8e1zjXmcTG5HPGFRSEoWcJWHh9": "INBRED", - "A8vTTQXH5qPHazNmEGrBnBvk2XYkj1i4e5AMVVN5aC44": "INC", - "3vqvkb7XBTRwBndj9V6WNiSqKGugWyMXLrdBwnkCpump": "INCOG", - "8469NEfLJC7oRBEQzpkBfYhQqw8chhMXkn7YwAotjupx": "IN", - "Fe5c469ADZyvnZrB8gdDsYt5eLKodaVYhYJCdunVr1nA": "INC", - "EQGG5muKhviWmWJwy4Fi9TeeNpJUX7RpHAwkQMnTAyAj": "ICC", - "4EsrfTaMr3oSNFUscBs9HSph7f1dJ26RyDJNQYE1efRC": "INDIE", - "HsBG6WGhbHYwyknCqDSZ27edyCWk1aPSVmmSYginhDhp": "INF++", - "inxKXw9V2NDZE7hDijzpJaKKUb97NEPJDTCEEiYg4yY": "INX", - "4J5HoZWoKcbo2JQxEEVCKRBfUQtEroY1QdRrKtZFpump": "IB", - "znv3FZt2HFAvzYf5LxzVyryh3mBXWuTRRng25gEZAjh": "IMG", - "T3aaDAiAfxZSqHFgyMxbSWRqeZFUDLHXvVAv1ZFpump": "IMG", - "2jcx4R6R8X2A8bqp4i612izx1s71Srubpt8MeZrHyBYt": "IMGX2", - "8JaibGAB3S6mUfgairHPnShFU29ujEmZTCNtzxRUpump": "TRUTHS", - "6BpsMNh6eCDvTH9d5SR3CjPnvB6GpMtuQVrpPthRpump": "INFINITY", - "5i6piokrXbNKxgzY17WAgoJhoEGYVULGQ2zgekyXpump": "BUTTPLUG", - "5rXSxYoRxASFrALkhHX2PG4D96J8L24c7DB3qEzRpump": "INFOPUNKS", - "D1wZHkfk8d6QsCjF3NTiYHLzsZJ2Qb4Q7WBFFbGuzBLV": "INFRA", - "FdvezWNEEYDPEQKK9FwCqMnaoYvse1f3m28w6okkZDmz": "QUNT", - "jNw3UW47CkLwmrF4gQzD6GrW7DhnJVBr6H3hZZBpump": "$TRUST", - "4FDtAagigMuFcPp36rbd9bzcYTJgQah2qLMYcYtfpump": "INMU", - "32UQH1VjtbvC4rNREPX2YuUEuNA5HUrELQS4auhupump": "ISENS", - "FwZxfG6WHcAAJD5ShYZZQNQCNgtrw2oniQhoDXR5pump": "IRLCOIN", - "4VC7UYqBo9Siw8ZnkPXfw9D3dzYCiVrPDzs9XRtyRJMH": "LABZ", - "DuEy8wWrzCUun5ZbbG9hkVqXqqicpTQw8gB7nEAzpCHQ": "FLUID", - "5V3vJKE4GKPNHdoU2SaWuDU6dkLdrY6MhpaE7tjwpump": "INTAI", - "cJpUMp5R7rZ6fGeLHbHhrRuJzK9mkyKDjZqNpT3ondo": "INTCON", - "XshPgPdXFRWB8tP1j82rebb2Q9rPgGX37RuqzohmArM": "INTCX", - "XspwhyYPdWVM8XBHZnpS9hgyag9MKjLRyE3tVfmCbSr": "IBMX", - "F7viRR7XKFeDxMG441DC1KDZu8cFjPvGFF6tnKYNVvFc": "IKB", - "DwmsWi1yWRiqXnbD6JJfGMQawfcqqBnRSyk6QEoGpump": "IMF", - "J9BcrQfX4p9D1bvLzRNCbMDv8f44a9LFdeqNE4Yk2WMD": "ISC", - "3REkm7R5HxCjAnNSMR8ktuv8Ghub71RxZzfWfWheBAGS": "BETANET", - "HRwo5GY87abyVy5tzW3g1RtgjrnxUEtWfDL7ftpjpump": "ICM", - "6UxB5HCuuNm2HoHCp4TFVkPPE38L5zktESYKZ7Q5RC3f": "$INTERN", - "AATECgWF4KjALbpgUXjabqFKHmdtjWdfdFNzCH3ipump": "ITMT", - "8SPRa884QcKbXRnXo449noBFek2iJRvxfeQaZSb9pump": "FREEDOM", - "7YKfh8hFbgHkwtAogfSWjsEGVNH5e1DoFF9Dj31U9CMe": "INTOS", - "4fJVpHzgaQ5F5BmFWpLrVf7zdmkYJccgcz6XMQo1pump": "INTROVERT", - "DiDWPZ7vQXfpaeQ8BX68XuDYeiQLv7diDxdeUpaondo": "LUNRON", - "1MGRpPrkhEsCm2GCWD3rsvEU77xTTLAzfKXeFgFondo": "ISRGON", - "CozoH5HBTyyeYSQxHcWpGzd4Sq5XBaKzBzvTtN3ondo": "INTUON", - "DbaSVQ87gQumixFumMHCrMKFpq5dSYNtKiVNDw3Vpump": "INU", - "invTrkA5SbM49yjk9EoknQ8DpBhaprTJ5csdkzTnAiV": "INVT", - "td1aY5AvYQuwGD75qNq9aPipMexraN9mQXJwqifondo": "DBCON", - "M6agiXbNgy8Xon9ngiW4ZDPbMFcNCTMkMMkshZyondo": "PDBCON", - "HrYNm6jTQ71LoFphjVKBTdAE4uja7WsmLG8VxB8ondo": "QQQON", - "Ffs23XgzXViCtB4Hhb9K1tHMi42RDbaaN8xVvP7YCggm": "KIETH", - "9DowFjAVk2svDXzFxcfuTgSTHjniW5D6TVaEmxFvpump": "INVO", - "BZLbGTNCSFfoth2GYDtwr7e4imWzpR5jqcUuGEwr646K": "IO", - "DDZQijTbaSd3Kas1r1bgCnHPayk8vTP8SfZWp5Tondo": "IONQON", - "9R1pCPM7GRr9F4gk978LqBQiPKfYStbZKc5iKV4imoon": "IOQ WALLET", - "21rweMLGYeMNonHW7H3xa5py17X6ZFRcHirCp9inRBQA": "IQ50", - "AsyfR3e5JcPqWot4H5MMhQUm7DZ4zwQrcp2zbB7vpump": "IQ", - "3uXACfojUrya7VH51jVC1DCHq3uzK4A7g469Q954LABS": "IQ", - "2ubH6Na9xWxXBWfNfQYqSvqLPUXRdEdAgCUcHXCKpump": "NOSTALGIC", - "13QHuepdhtJ3urNsV9i1hdL8nQoca2G7ZaLzb5FYondo": "IRENON", - "T1nEKn1kMyMKD5D4uvXe7VD5V86D9yu6NegK4xnBAGS": "URI", - "EjR5wxxVmv3zKbhawpfJb2f1GeHWsQjGQX6JVDgLpump": "IRRAWADDY", - "C19Q2Mvr1icQVxQJWpDTVDJjLTzAcXbUt3bPmBsYpump": "ISAACX", - "HjrN6ChZK2QRL6hMXayjGPLFvxhgjwKEy135VRjondo": "SGOVON", - "EEy57xbaLcUrN1HXj2vz8VWxeWFK1eZQZo4aWbrondo": "SHYON", - "KaSLSWByKy6b9FrCYXPEJoHmLpuFZtTCJk1F1Z9ondo": "TLTON", - "D4uWxzR5StYC6sTRhVts8Eboy3pmVtHeNC62dnQondo": "IEFON", - "t71FyTYHVkPAb5g48adDHmkVxXYbUuP2eq6jDZLondo": "CLOAON", - "6JLG8iUkAuqiBhL3j2ckDMDf5oWAa6awmyaWezKondo": "IBITON", - "CeFbGYXDmkyfo1TXXzzZ512mtnCCewNohu6V15vondo": "FXION", - "C9J9vZ8N79GzzxFoRkPWCkGtMKU8akg4FhUk4r9ondo": "IEFAON", - "cdVNL7wK8mf1UCDqM6zdrziRv4hmvqWhXeTcck2ondo": "IEMGON", - "CqW2pd6dCPG9xKZfAsTovzDsMmAGKJSDBNcwM96ondo": "IVVON", - "cfPLN9WXD2BTkbZhRZMVXPmVSiRo44hJWRtnaC8ondo": "IJHON", - "CPWkMURVvcnX8hGjqCTb8i5LkzV3VSvyk7SeJi8ondo": "ITOTON", - "13qTjKx53y6LKGGStiKeieGbnVx3fx1bbwopKFb3ondo": "AGGON", - "LitNUakTges74cjDJm6HHfFNKGPdySkp3MWSYzYondo": "ETHAON", - "mhZ69E1vDnAsQJXAwarLYSX5tmgeMajXBJ2rXAcondo": "BINCON", - "M77ZvkZ8zW5udRbuJCbuwSwavRa7bGAZYMTwru8ondo": "IAUON", - "CBKcmEvVg5EgE3W5hVSPcBYWh6TFVjQwbmYod9Pondo": "EWZON", - "BmXVAFyfpW7VuVYeWDtbFtLx7sek2mZt3BEsGgAondo": "ECHON", - "AbvryMGnaba9oADMZk8Vp2Av6MtczsncGyfWaC4ondo": "EFAON", - "916SDKz7y5ZcEZC9CtnQ5Djs1Y8Yv3UAPb6bak8ondo": "EEMON", - "DBNwt3FoYCKQWdfzxKFNZ4mzuz4Jz1iRzFf7HFzondo": "INDAON", - "C6c7VcxuUYcV5YTsky5HM4PUmfwHTwsDD5DNwwPondo": "EWJON", - "C8pSaSgjkiTWixS3GM6Hxd6HKnKrgAbY9WDgfVeondo": "EWYON", - "dSHPFuMMjZqt7xDYGWrexXTSkdEZAiZngqymQF2ondo": "IWFON", - "dvj2kKFSyjpnyYSYppgFdAEVfgjMEoQGi9VaV23ondo": "IWMON", - "DX7g7WNjDpVzNK9CG81v7wb6ZbiNzYfkdzH2Xs5ondo": "IWNON", - "EN5pHc1LccUSojxb7kkyQi7v7iJN5RpDq6qz3DHondo": "SOXXON", - "iy11ytbSGcUnrjE6Lfv78TFqxKyUESfku1FugS9ondo": "SLVON", - "XsxAd6okt8y1RRK6gNg7iJaqiWNiq5Md5EDf3ZrF2dm": "SLVX", - "k6BPp2Xmf2TYgrZiUyWfUoZBKeqaDbvPoAVgSx2ondo": "TIPON", - "DDcAL93Urf7KrPntvKULnZoFs4Wdee1LkkJqLpjondo": "ITAON", - "HX6zNkjJ7zy653VoDWzbaYpSg7BrgLfq4i4RA7D5nkkz": "ISLAND", - "ApEKQzeVsGs5bk9hR1xKUEfoFGzvve2Aszkn4ft5moon": "ISO", - "BQX1cjcRHXmrqNtoFWwmE5bZj7RPneTmqXB979b2pump": "ITALIANROT", - "GuVoE2qAS3DHaAGSeuZfBkbLjFXfP46DFbogbrVJNHfN": "ITA", - "CpdKgsZMtZ54FK5vbyLq5wNyH2uRmSxi8AtEAADipump": "IT", - "yoPRbNPxDHFF5tfT66GP6E5SboxYjnuGWFW859Cpump": "RISK", - "iTHSaXjdqFtcnLK4EFEs7mqYQbJb6B7GostqWbBQwaV": "ITHEUM", - "AtF6UM11gM8pB2pcoBLDV5DDwpHPDzgYEKB8oNqSVee3": "ITXS", - "25vHoSeyJeroPYaTf72o97rCKmyEoGK7iNNHDAv6zYy6": "SHRIMPLE", - "61uiuu5aAc2HG1ZQaHd3opfLnK6cM4FQnE1jRi28pump": "DOG", - "2B5Rs88FYyFp4XciFrJ7zovvEeybiinZA8PXsSptBAGS": "DOG", - "GbQXFoyPTKjWvtj7LE8efXvjThSqYfnvmNYn6Vccbonk": "ITTY", - "6RFfHjBTgjK3VjDTH6GQ7FN3EsYjjxWcTdExaubzsx1W": "EARLY", - "146Pouk195TZZBFBc6f1gisFsgsKFviPbwbbV84AfMqt": "JACKPOT", - "DKnXmFGBcpYEnPsoHLrdj5cbZdEq9DV9z6bVfYrrDEBE": "$JACKY", - "8cNmp9T2CMQRNZhNRoeSvr57LDf1kbZ42SvgsSWfpump": "JAIL", - "FBbnzHwJ1WHYwP425cqMNb2t7o7sm6AXQjsQ3sZRpump": "JAK", - "9g7hrNJUUwgLznSxA3X2nexCGhiqscBq3nJ3vSfkpump": "JAKE", - "9em5B5cjY7VYGCcEuqnbSDLViezXFgwhPDiAXTospump": "JAKPOT", - "Jambjx1oJoZNBZiqbiF9TqgatEZPdyfvYa9WVsKNzUh": "J", - "2cMYUjUQJzrTcnxrD8JgL1BQL1AQKCtRkYLdmaTpCWYB": "JAMBO", - "51zudBR4NmATG35goida4dLQH5YPn9k8hVkLcizNpump": "JAM", - "E9uUgGXJ77AVmaqVhN544oz644VUPBGU6r4qUaeppump": "WYNN", - "6f8V8b9eG5Bhy8iVedzdrX7LyoQKk1Ag6G56XHfYpump": "JANRO", - "KZtqx9BJbpcGY7vdzhqPXM3ECKChxE5YhXaDiwRondo": "JAAAON", - "EHCwJQi8dSpZfKm4LJypzozEj5vAN7pESRXJGpESKMfJ": "JAPAN", - "CmpuL8k9KY3NrpfDRoJrVmuwd1zRMFRUxX55avyGpump": "JARVIS", - "FR382Gv7RxrEb2q1MZkP9athyJC9rUBt8M2jKfyKpump": "JARVIS", - "6SUryVEuDz5hqAxab6QrGfbzWvjN8dC7m29ezSvDpump": "JASON", - "HLQR9Rc7rzfiydRs5qaJGoKniu64zCNN75v5ju4HF2ap": "JASON", - "JAB57kZjbmqcmaRtgmbeoeGtCjnotLowKGSCfgBMpump": "JASPER", - "9VY2rDbtsBmTsBxoRF8hWSEUKGqnoQoe9V6W3JnjNgfm": "JTVO", - "9e75hhwsXQkX6bsHAuwYAQs786XXKnvfReW3gKEcBAGS": "JTVF", - "E1aUS5nyv7kaBzdQzPVJW5zfaMgoUJpKYzdnFS2ondo": "JDON", - "GAjvGwTZok2HHSUPi87EN6hCv1TeQMjokbru1y7Wpump": "JEET", - "HPHau8yipXRgJShyhFxBpeaAfCc5rzkb3KduuRErzZPh": "JEET", - "DTLYtCJ6cBHbA1ZTkkL9SAVmUVemdCCuH2QeNNXPpump": "JEFE", - "6ZaQ5yKKbUbNpocw9XaTQUzvsucTcDekofsqAvFRpump": "JEJE", - "GFreY9SAUz96P7qkF19A4dtA4TmZgtL9Gmu8gV9Kpump": "JELLYFC", - "5z37F9LJ4evEn2UncteJAyPgoJJNtCPGgm1onNespump": "JELLY", - "412zDygnwP9DzitnQVgRKUFFTDmrYScFch6P2k39pump": "JELLYBEAN", - "FeR8VBqNRSUD5NtXAj2n3j1dAHkZHfyDktKuLXD4pump": "JELLYJELLY", - "EmNFqmSi5DvwGjW57LJ7J5i8R6T155JrNf6GNFaupump": "JELLY", - "C2nneybCJP2i6mk6Nu6SfTcxoNXNLjngnzwDgje2eNrr": "JHH", - "3psH1Mj1f7yUfaD5gh6Zj7epE8hhrMkMETgv5TshQA4o": "BODEN", - "DtgDZb83TqywcuBuWE89jx4k5Y7b6nQ4GYJq3Wd61JQQ": "ROGEN", - "FQddEUoqKPnAsc4UTkAdAUV3LnCXoZEJMazguC1N11kC": "JESUS", - "2c8TQe4TUf2doRXd3yQ1XEn7zhT1vWw1y41SXz6G4j7S": "JESUS", - "FotrwPuTNxP2Rx7DEWVcM3wVNaG1Fs3JV6nW5aYQ9AFF": "JETCAT", - "EFY8GToe4HrmpYrRpfRUgNEPmfVnUbZrsUhd2mJQpump": "JEWELS", - "GT5qW4HA3hzfdioeTRhSQpdhcpZWZcDP3Xj5nXraTiAQ": "JIFF", - "94XduSfSnyas7jAEFSJSXiCi1xQ4mENWcii1aCvjVuqu": "JIMMY", - "Gaqt6hKqBSRga8pfJ43CfTbc9ZGw8ostPnjytfbNpump": "JINDO", - "WrDYG6miZG2UcXAGN6jQ73ukbigGEHbZovjY7GVpump": "JINGLE", - "GgzJWhQWTpv2VHqvJnx5uEWQzNkfQSncDEWUdgQk4fvh": "JIN", - "FdpH2hc9kXewsvNtrbWbgtWXVHHFCe2YbjE7S57upump": "JIN", - "B4eppPm1Dj5Nd7KsN1cdJfQgWSSqqSfi9BsNZYkUkXm2": "JINX", - "jtojtomepa8beP8AuQc6eXt5FriJwfFMwQx2v2f9mCL": "JTO", - "J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn": "JITOSOL", - "8WNzazee7WbPLDHi47vt3CdXVNXJnigLjBMmGmXgpump": "JIWA", - "AyrQpt5xsVYiN4BqgZdd2tZJAWswT9yLUZmP1jKqpump": "JOBCOIN", - "CXbypcjnbV7A5b6XqfkUCUkLiD3czHXoxAsWBYa4pump": "JOBLESS", - "6TUJXvuUrS4rnnm8LmG23AZYsWQavY9SpeAG3Cdjbonk": "JOE", - "H2ZpBXtzk1DaDTzsoXqMhjN6Bd3qzgefHjUZ1e2zuwAe": "JEOING737", - "JACSU5f2fCsQSCDNz1VX2Se4vmQyj8k5EYigD4RppvGV": "JOEL", - "86QASyebqezuPdbbkWyYMK5jmTAL1td1SuqobeKEpump": "MCAIFEE", - "2nCeHpECQvnMfzjU5fDMAKws1vBxMzxvWr6qqLpApump": "SUEDE", - "CVGBG44dKpUFQBANot9jY9KQ68QCBXAwd4sf7BJxpump": "PORK", - "KUXt7LzHWSQXp5eyqMZRxWjAP6yM8BUh4LRHwiwondo": "JNJON", - "XsGVi5eo1Dh2zUpic4qACcjuWGjNv8GCt3dm5XcX6Dn": "JNJX", - "BuxH23osRyFFLbWG3czrTsfBQYbxzVZ8f7QV4cjTHN5x": "JOHN", - "4RwVoEESmPWWXG1wJGgpbapYsRdfB2Q3mVHkYXtLbonk": "JOJO", - "H52CAqEJXY9dmPJChvi86cUR3vLEobUhu7B9wBMppump": "JONAH", - "BHcPVARUJEV3rCAmbLgRm7QPmZotsCcHcKWwzvCSAHJi": "POOWEL", - "7ty3ruNJebb8RYmzfpUommJnZnCwaoB8RNfqpjLMpump": "JORKIN", - "HZNnmhAY6xfq2iKRyBTEvTVeoTYJzpkK8mfnfG8Ppump": "JMONEY", - "JPGCPKWfGCEMRraSKhsCcn1qvs7qVaLjmq6ihveXn62": "JPGC", - "E5Gczsavxcomqf6Cw1sGCKLabL1xYD2FzKxVoB4ondo": "JPMON", - "XsMAqkcKsUewDrzVkait4e5u4y8REgtyS7jWgCpLV2C": "JPMX", - "7Q2afV64in6N6SeZsAAB81TJzwDoD6zpqmHkzi9Dcavn": "JSOL", - "orairHM3Yw2PbTfCty1PXy7tEUx3uBMfjouNbm4KnRJ": "JPOW", - "AvL9rHWzJA8G8mTeipKJ3Y4XBzdeXMq1P5nNEBt9gem": "MENTAL", - "2LzLh5pHg3nDQz6goTLAvDXfDbSBgAR8qem3bdXdpump": "JUICE", - "Gb9jGTUrGLvqHacsKDXbxsbEr6pqZb71J661WkBFpump": "JUJU", - "JosjEXh69RckgSs2AWsN1xN8zmiSHxBuJjHLURJnHhg": "JOS", - "8rUHbf6cP8ufxqKQUuRhhM5yyDKPr4tN5e5ANz8Ppump": "JSS", - "9P32yqucXfZnDoXmmJNzBpqXQgfou4tA4sB1RaQh9cYw": "JUNGLE", - "3UQNqYwBZyZz7WNmGSoyjYLuc8meVnysSCYBDsQ3pump": "JUNKCOIN", - "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN": "JUP", - "27G8MtK7VtTcCHkpASjSDdkWWYfoqT6ggEuKidVJidD4": "JLP", - "jupSoLaHXQiZZTSfEWMTRRgpnyFm8f6sZdosWBjx93v": "JUPSOL", - "JuprjznTrTSp2UFa3ZBUFgwdAmtZCq4MQCwysN55USD": "JUPUSD", - "AbBEjBdz31yCb9gTFfWgEi7uprCzjTEKL58xqHVbbjhu": "JU", - "83SDWc8CaGWwMTFwo1pmktB18SEH84DLbD9AikG1pump": "BONEGUY", - "HFyN8wr8B9AY5VeWgWTaWxSowVjpkXnDjPJ7DJUrpump": "CHARTGUY", - "HyU6PuA4oDPxnEpP7E3MGc4u6MQ4m31LZihTF7RPbonk": "CRCL", - "Fshf9niKKQbAS3KjAxkyhPTTyBcW5k3kVE6DhB1fpump": "COIN", - "3tPqGuHeHcqD7xgAPNSCv8Wxfb8Ja2v75MQWp6W4pump": "DEADGUY", - "FWGWrsrvgcz1w6LjSSnuHB5y6KyWXhUKjLWhvDSJ48hE": "FROG", - "3Cmv9wxP1yNQZErQEFabB3L8uHxz49AtCRMrbKnBSozq": "LITTLEGUY", - "bwCs4bAMoExahc8Np2rxufxfrGai2Jb3mwkMqxkpump": "$LOTTO", - "Bb4jR951QtVjeFAYFLBYXDSMKjbTDroCLPbFLdd7pump": "MEMECOIN", - "wo1zgt8rfrYpvdVi4nidoj1SYfcR4pQx69bmNv2JLhQ": "EGG", - "GaPbGp23pPuY9QBLPUjUEBn2MKEroTe9Q3M3f2Xpump": "SHITCOIN", - "9eppz2tFJRStziW3Hgjxu17BDAdpD2Jat4TxMHCRpump": "USER", - "3LZq8TKNtX2kT8X1H2A4AHcvhUjQ5JTxYWTTc5JBpump": "¥1", - "GHichsGq8aPnqJyz6Jp1ASTK4PNLpB5KrD6XrfDjpump": "$1", - "9X45NjtGbGo9zdCFmMyqZyNzC6Wa67KFbfvGc8nubonk": "DETECTIVE", - "CQDfbCK17ExoXA66nLS2XFrtwPVDhQJDdFRWErjx3Zba": "JUST", - "FH5sNBwBNNcGdkZmB6oHkoTH7cjEpVN9hgfhoRC6pump": "CUZ", - "6RxB1KdzVMXrrYn7nQWvaGgh5e2EpbA1FcEFtq74pump": "DELULU", - "DiiTPZdpd9t3XorHiuZUu4E1FoSaQ7uGN4q9YkQupump": "ELIZABETH", - "Dz6Pw2zMjXH4pY1bCkUeHvcTfrKGH5fHXZTgTUJZpump": "$HONEY", - "8iMCGF8ynFMPj4z5QhYprgRDnuE2yk8KbMdZAScbbonk": "MARLEE", - "4kNS9EDwpiJNBJpUCQ2eYcZkuj5Xo4cco4jsrwk9pump": "PIZZAGUY", - "DGagMywvLG3DwffZHX4eWWE6svnoJpiod3dSNBDwpump": "JUSTICE", - "M5MFPbS4X6eYek21xhFU38DTavMLuGY9eyC5p8Tpump": "ZACHXBT", - "HBV58vtGSRWDppXpaER3KCRU2SyH5Z9YPTF92PTwKvzq": "KIRAI", - "4daoTLufDmV3ods48Zh8rymaZKBLtgEvuH9qALYLbonk": "MEMECOIN", - "EtKq5MVm8vFFVAYHX9Y7rYMQu2aWfXDxt7eEnZDjpBvm": "MOON", - "4xgZmdKKHE3PXtUwUtvJGPSCSTJMJohSmWufJhiVo6q": "REST", - "AK87oZM8ovFU14bq8mu4ufi5zsCPDbqqVvh5b6WHbUdL": "TIPS", - "JWmDeJQwXBmTinz7EQVmMMBpjXNU3yHTEQY7Bhy9UD4": "JWT", - "3FkfFXtMDVkKJ6SAouMSK4qYFmErYxSnAUx6vP52pump": "$K8O5", - "F1uB7phqFVuyzsFoXWkcwNoCY3KJGFNsFLyeSoQYpump": "TITAN", - "4CKd1pfak4Ss3EX9aHqz7hAkwVdhTqtrDKNexwq1dFxY": "RWT", - "Cm1wUvp5x2GuSkU3F9q3GoJaFvVWExx1MH8jEscWpump": "KABOOM", - "3h4DGQd9k5gsiMnNzDuGXAcjkMm5G7iC3knykdvBBnxU": "$KABOSU", - "FJup6BbEBoCeFJZtqW4qcaqABLco5SkV8683do38P9tu": "KABOSU", - "H758qwhw8YHLbDYmJsdN6Jta2ioguZ53PY6Qcisupump": "KAFKA", - "2MuDS29b6rQb9MydKLMvggST5Yqez3B6gYWitvvjc6ir": "KAI", - "AaoHhEyupbhaASkCCr7AgEvZh8AknSuDFwgw1nskpump": "KAILY", - "B6dvdDUUYBinNDUT4xsHBSCNu2SKb9P33Z79ySakLREV": "KAIROS", - "kZEn3aDxEzcFADPe2VQ6WcJRbS1hVGjUcgCw4HiuYSU": "KZEN", - "3ejk8LXAS9kUC7XhpDGHRjARyUy5qU7PaAq7PMykpump": "KAIZENOS", - "FH6jc68WzeAUXKp6uDg9QPciTeU75o32xFDzKLzmbonk": "KAKA", - "PreLWGkkeqG1s4HEfFZSy9moCrJ7btsHuUtfcCeoRua": "KALSHI", - "6BuKbBATFnUF34g97Vtni5Xq8qNyzYD412SaEBoStPKU": "KAMABLA", - "HnKkzR1YtFbUUxM6g3iVRS2RY68KHhGV7bNdfF1GCsJB": "KAMA", - "KMNo3nJsBXfcpJTVhZcXLW7RmTwTt4GVFE7suUBo9sS": "KMNO", - "Gy55EJmheLyDXiZ7k7CW2FhunD1UgjQxQibuBn3Npump": "KAMIYO", - "4yTZ41t1WUPJoEFwhmFFzrL3UbFWkdDRVUKJTtLwpump": "KANGA", - "CQSPU12VppGZB7LeRkEaKzrRBzvo1LyA9mR2ydiwKHaL": "YE", - "doPqjCxi6UkANkvMz5fSuYGEo5PGppVpTZMeB5vondo": "BZON", - "CEhFvMotKm3zucKUBEHVvTaxQ4e9QVPaAjSfkzFLpump": "KAPPY", - "EdN7mEndu6esJox7WxDdPhGZhmhYkFN9bTgsAxnNpump": "KARMA", - "Ta1wrnVmwJ25RpE4KN7z4YNMvBhSKgi4U6uu9xEtime": "KAWZ", - "A88EaDaxmui6kGAy9MqEhLSpx1Ds4bHcPeK3kNVXpump": "KNU", - "5Gb67sU9UgZsrbhrbKs5yYtWLFPwPLYKzRQuUEZfmoon": "KFR", - "4ZDFnVXL2ED9htVnLTNMwCwVttp8TbBJcwzFePZwpump": "GAMBLE", - "2fZTwhbLg3xPquk8CqS3KKYbrNMbhKj7Q5zBKuT2pump": "$KEK", - "3HxDM7xreHuop94zzzVP39fTKLvJCwHcSYgrSf8cyg5M": "KEK", - "Gp8GVGPc8QCe4Jn6ryG5YKokG5bjKycATEzqpeyspump": "KEKE", - "HuAncxDEsakCDgZS2Yfo9xJbHmtHXMnxxkT9jqdXnHhm": "KM", - "HAVVqPQf4MKVjvuFVi8y7g3xkArdWQemaJPBvKmEpump": "KEKIUS", - "FThrNpdic79XRV6i9aCWQ2UTp7oRQuCXAgUWtZR2cs42": "KM", - "XC78JjRHnSqaysrHiTrhR8eRngFbvX3rrHyRjWEpump": "$KEKIUS", - "6P7xxdfAZzY4AYZuZCwcfgQLaXpDnkxYjPjrBN3Zpump": "KEK", - "6HP3PZ5DkCy6kEjS8CKPfeMCXgoxyTcdvEakzFyzpump": "$REALKENDO", - "2nnrviYJRLcf2bXAxpKTRXzccoDbwaP4vzuGUG75Jo45": "KENDU", - "53oF2Fg2RZcVKNPwzQ5s7eqtF6i7orGWBpNkBAZYTTqD": "KENJI", - "CEY7Xa21gE8fh3ksS43STCftHHJNnSE2seF1Vscdsmth": "KDT", - "9pPE1q9EW1bMQWbHmffrzUCfRr7S82UoxNUFfA6mAZC6": "KEYCAT", - "3fGoNjPohYRVPk9iJuwEW5WuURU5gmAdSRgNgeaanexQ": "$KEYDOG", - "4KVGATyKJNhm8y8afj5314rNAkB9MZGJVDE5cCKTpump": "KOL", - "AiQcnL5gPjEXVH1E1FGUdN1WhPz4qXAZfQJxpGrJpump": "KHEOWZOO", - "BxBkTCHsrcYChcwB459CAGPX9x4fWSVnmzqh5efgUN1R": "KIBSHI", - "HhCLbkW6FwhriTkk81W8tYstsRCLUu6Y7Je1SQjVpump": "KIKI", - "npB9cxTzwUiGt7jk2dXZa52xZve8SgVD6Et9Bpipump": "KBBB", - "CFYhdWXsYfS7swnJgHRwb5toTHMEQD7Ljd5gGwKLpump": "KILROY", - "HaUXAdAWWUkb2WTmh8z1TdaEmZkYjnQnmC6tHUdTAHvi": "KIMCHI", - "AWpD39myXc7emw5M9cMCofkbo92x5FKWdGHWYpZFdoge": "KIMCHI", - "kinXdEcpDQeHPEuQnqmUgtYykqKGVFq6CeVX5iAHJq6": "KIN", - "V5cCiSixPLAiEDX2zZquT5VuLm4prr5t35PWmjNpump": "KIND", - "CVfniqNEj2f4Yd8Z4TEtaTU49gWNTwUyCiDDUbsZpump": "KNS", - "9noXzpXnkyEcKF3AeXqUHTdR59V5uvrRBUZ9bwfQwxeq": "KING", - "HV2tLSVHRk3Yx76GsR6dGRTsebbkJJyY4ujhLAyf9T7E": "KING", - "9BySdih23rwDPZB8auQXX9k5u6a2Nk4GSDji2MB6pump": "$MYCO", - "CfVs3waH2Z9TM397qSkaipTDhA9wWgtt8UchZKfwkYiu": "KNET", - "GZxrdsiXEuGBkUbhAov394489CtiF9LMA7g5HsdVpump": "KINTON", - "87n4vtsy5CN7EzpFeeD25YtGfyJpUbqwDZtAzNFnNtRZ": "KIPPAH", - "CefZxozhhxK88XPJoeWBczYSaBPd35tsKnziTH6Cpump": "KIRA", - "AY2Qidgrky4TFymeJKe36w5buY7QWK7TSd7obQLupump": "KRA", - "3Ei8SaoL4JWZv1XsWePqiAjVtb7QtpJbV2TSuURmpump": "KIRI", - "JA8JfHs6v8ZXmz4MDFaqgEJHkxWxRnXfVbXidm1sTGCG": "$KITA", - "CyUgNnKPQLqFcheyGV8wmypnJqojA7NzsdJjTS4nUT2j": "GIL", - "AUgdt7wjBifF9vZpde7BjU6HLroCYh4SUHYc7yhheECW": "KIT", - "9hfKxLSnwh4LhpRgmxBYDRgrKeYUgszMePKxuidPpump": "$KIT", - "DN1sKvPYqht7Lx2AkhsVeMYGR53rVav1iJzygmstpump": "KTE", - "7rCrPCf5qTcEAvDC3mH89rjZMetpRDLXZBvqLoPPpump": "MEOW", - "3TWgDvYBL2YPET2LxnWAwsMeoA8aL4DutNuwat2pKCjC": "KHAI", - "6Rwcmkz9yiYVM5EzyMcr4JsQPGEAWhcUvLvfBperYnUt": "KWIF", - "8yJ15ee2AUQmwbWPxXLTTeBTzyMGn4MtSRKMqVHw1J1G": "KITTY", - "41fZewbrb8x24yE9KeMJExoVXCDggebPMEwRVvX8pump": "KMC", - "72LTryHMb1m9j4NEcn673sXkFMXMmHvmB8grYcVspump": "KS", - "149o8ppQf9SzKCKXZ4v3dzHkwumvtQSRzSEkr29uondo": "KLACON", - "1zJX5gRnjLgmTpq5sVwkq69mNDQkCemqoasyjaPW6jm": "KLED", - "CeFrV65BuLHSsa4XGjaJau1af8Jr8e6Kxqc1iixwpump": "KLOUT", - "5ritAPtFPqQtEFHcHVqNjR5oFNUJqcmgKtZyPd2AyLLy": "KNOB", - "LhcykxUt6xVdiSZyWyaGdNHE3W8sAEmdnEjpMdkpump": "GG", - "7RDvypx3p9EWq4nZZKux1ZQAc7DUWXpHTVKxCCnupump": "KNOT", - "Gw9saRvRTQUyMmYBabsGibceFaFe4RKK18nrbWYZpump": "KNUT", - "ALWi1o3qgXp68UyyDkG9EvL1Cq4ofhLsRbDJYbMkpump": "MESSENGER", - "FsA54yL49WKs7rWoGv9sUcbSGWCWV756jTD349e6H2yW": "KOKO", - "48iJcUv9jsiZ7cCisyVFLPFLMoNBKg3L43bRvktXpump": "KOBX", - "HmfNGq7kxE6ppMDGW87xPuMU6wnKbeYBZf76K7t33w3s": "KOHLER", - "6fdCC8xfrXNy6PmNaVcxdEY5XNCTAha2V54zYYnmBCey": "KOI", - "5HkhVG2bSb5PGjhX5QHm9urUquD7tx5eAau5Fonq78zc": "KOKOK", - "HTWDJbpi6o9Zd7SuVDBRo5fUPXgffmo27f954Vzipump": "KOL", - "6U932RLNzovLyi5rFpc1K72yRCSb2dtwMcqXK2svpump": "KOLBO", - "4q3Z58YxrZEAVMLtMwnm7eHtodSD3LSpSNt3pDnqpump": "KOLIN", - "58AYn86ZvgwfTJo56xpnPQ8u21W2NsK5QhnSuMyBbonk": "KOLS", - "6jTQCFZR8JwvvenVGa3RzGM3a5YEagk9kQXDpHHdpump": "KOLSCAN", - "3M2vepByfZTG6xUSmFidvSCFzHWFoVw7cPvyRDnUpump": "$KOLT", - "DZpa4peCErsNzsYJ69XYYTSjZGDQhuexnzj7EiZ1pump": "KOMI", - "EdkMbLbaHZNUSez4Z36t8fZbHwtQ4Wt34Q4YLx2ipump": "KTO", - "9GuqvJbdtSrK6JNHKTvi742T75ShGv1R1ieTmEutpump": "KONEKT", - "AhP7ojZMRMMcmxwr6S1UubGWagFbDuU36tTj7ZYypump": "KOOK", - "3H4srZRfFDkp1MxsVcRreMYVmKizFz6B8YcXWv5ppump": "KOOKY", - "8neDcPFwrvRNM9WXHCtHwTcKZrVm6wr4cs4PSN9Hpump": "KORA", - "HtTYHz1Kf3rrQo6AqDLmss7gq5WrkWAaXn3tupUZbonk": "KORI", - "t5cSTUSZzUAQXQKzQvhieFG4Hz4xC23z9du1Chp8gES": "KORRA", - "D1mM9THeoBEa2xBbRHpinBPc9x6rDeKUYzMvcUDzpump": "KORU", - "SUBXNGpJq2kseayQihcqTjfc8EbT3W5iQwjXFQpbonk": "豆柴", - "BfdVHnbt9LSNAFCZU9kvTjbrH3jX78sv2siLKGQ7pump": "KOTO", - "6kreK2oNJpVweA4wXA5vUsyhtcXxDhANRk1w3BbCpump": "$KOVU", - "DcUoGUeNTLhhzyrcz49LE7z3MEFwca2N9uSw1xbVi1gm": "KPOP", - "U1zc8QpnrQ3HBJUBrWFYWbQTLzNsCpPgZNegWXdBAGS": "KR8TIV", - "DFc4kvax4JYFePaQLZeypGREyeWkeo2Rk7JjU5pS4R43": "KRAMPUS", - "DVPSYdqWPLvNa8afnEqa3B9eDfTTWpGyUZeXvdMondo": "KWEBON", - "XsAiRejKuvLAdq9KtedrMSrabz7SWdzKoVK6Qgac1Ki": "KRAQX", - "F2hgieC18MeUWaWMoyaxLSQCzS7yAjRz1HgmbGAEpump": "KREDO", - "Gw7M5dqZJ6B6a8dYkDry6z9t9FuUA2xPUokjV2cortoq": "KRW", - "A8h78muegtuonUxqAhpmWcM8pJskYsTW4Xp5C33ipump": "KUNDALINI", - "7DJ8Ufmcm7Mh1HCK7C2ZA4WiCnhocecYeJvAGv8PLdQC": "KFUCAT", - "GQx3p7aTHLQHDqzFR3c1QSk1Qhy2hz4YbAnkjdXtpump": "KFH", - "7H2k7iSHcAmZrXZ1wv9CWRzJbS19C3LZtEd71XRhjBLV": "KURO", - "2Kc38rfQ49DFaKHQaWbijkE7fcymUMLY5guUiUsDmFfn": "KURO", - "919MM61rJb1KairDkbNoH8iCVkX1fGFn7w4Dj6Mapump": "KUUSOU", - "9Yt5tHLFB2Uz1yg3cyEpTN4KTSWhiGpKxXPJ8HX3hat": "KWANT", - "DEf93bSt8dx58gDFCcz4CwbjYZzjwaRBYAciJYLfdCA9": "KWEEN", - "kyrosJC2dtm6EoLV5wffZsS4RZVm2hRafKZCLsc38JE": "KYROS", - "kyJtowDDACsJDm2jr3VZdpCA6pZcKAaNftQwrJ8KBQP": "KYJTO", - "kySo1nETpsZE2NWe5vj2C64mPSciH1SppmHb4XieQ7B": "KYSOL", - "HjdE4j3rHC7YMxFMHXSgEzXoNcVU6YBCfg9Q1gTJpump": "KZL", - "CMDKx3TGVJryRDQ2MnAkTRNSyguQLrKgiCPZ3Jc6r8r2": "L24AI", - "E5MBirnq71DthjqwsAsgvCtgCAzShoWqnrzkmVQKviJc": "LABI", - "3nSMUVeDed6G67RwLvBWxiYU2qEGVJXf2TD5MiTRpump": "MEMELESS", - "H57gd4TRhsmhg5brTe2bygibAKp2btwS1uEStoUrpump": "LAD", - "7TgfC7finEVA1QcCbn1Jb2cz4RiDL4if4ATEGHZupump": "LADYBUG", - "Fj6p7eodS67517JTEJeYNN54bvk9fRK1Q7oC4pAupump": "LAMA", - "LMFzmYL6y1FX8HsEmZ6yNKNzercBmtmpg2ZoLwuUboU": "LMF", - "LMDAmLNduiDmSiMxgae1gW7ubArfEGdAfTpKohqE5gn": "LMDA", - "FhAdp55ngkaRCC3TGAMsJdDCUHQDPTMcxQDJ4HCfnxMf": "LAMBO", - "GQsefQc5YPE12wkMnYMxD8Rq1wrRX7f9iFgpBjNpBAGS": "LAMBO", - "wFJoeEYpKg9oRhyJy6BWTT3J95gmXBLvoeikDQNondo": "LRCXON", - "43uhykFm8Y9gLvHrWs7r7w1HCKu6vikDi7j394FaSfNz": "$LANDLORD", - "2qRbtXPMx9LGcgk7TTEHbbemEr57ABdKsLbjxUSwMbJd": "WOLFINA", - "LnTRntk2kTfWEY6cVB8K9649pgJbt6dJLS1Ns1GZCWg": "LANTERNSOL", - "8PH1XWXyhvFWXWeixSbcWmfHX4uRJHQtcjnwkCgzfTkY": "LAPUPU", - "98mb39tPFKQJ4Bif8iVg9mYb9wsfPZgpgN1sxoVTpump": "LLM", - "Lrxqnh6ZHKbGy3dcrCED43nsoLkM1LTzU2jRfWe8qUC": "LARIX", - "CE5hEYzTBwUP5U2uDcmdfGHJMh7oh7VVPvKNP3Ffpump": "LARPBALL", - "5UmDWgyLV1JBg8Jr8NwyezXdQkiU3vHGJu2efm7Cpump": "$AGENCY", - "6m6TjSDEbbpcEYEyvb2ksdRd9CFYENRU6yAZ4J7jpump": "LASHI", - "6yJbrQLd3N5xKgz4PLjADwxnNZj7N1g5bWXjz46Dakhr": "LASTCLICK", - "BnszRWbs9LxSzsCUUS57HMTNNtyDHFsnmZ1mVhAYdaos": "LLM", - "HfqS59Gc3CjMsLTUEcWznkcsKBUpqZJ8FTyBU2jhVVKX": "LNCHM", - "B5boCskXEFb1RJJ9EqJNV3gt5fjhk85DeD7BgbLcpump": "LAUNCH", - "86ZnAujEVLmtnNazeCeT1zYR7hn2PeF5ZPEwUkTdpump": "LAUNCHR", - "5yes8vgfrsqP31JZnFxKVU8A5X1brigqkxySYvgS5kLk": "LAAI", - "F15Vp1vqJdQ58Usk4T4GDcL7YGegcp1pznHrpt9opump": "LAWN", - "9RAghejZNirG4skdqUXxVVSnKGmYSjjjgnPMPjyJmoon": "LOA", - "FFm8NUwVsDpfaGvWUyL9xcAmk5cdnjHq1tb7eBzBRTZv": "CODE", - "5k84VjAKoGPXa7ias1BNgKUrX7e61eMPWhZDqsiD4Bpe": "L3", - "HeFNBDauVZDD86dV8TmFGx39kGxmFLrVKqbYe6wXWcPT": "L1X", - "7iqRq48RjwPzXHEarqSiYW53jqrKfuLPJd8z6S9Ybonk": "LZR", - "2JYuVHdmRtKu47xiNgff5eTJwxBFpTnBFkF8HYJFNL3S": "LCP", - "43YakhC3TcSuTgSXnxFgw8uKL8VkuLuFa4M6Bninpump": "LC", - "8SpPaFLycx897D6sowPZkEkcNdDahzRZb5itr6D8pump": "LEA", - "52axFFPqNeHHL86yANnRpu8iedLjx71iJ4LZJoDopump": "LBW", - "Fdp3GvvkJZkaKLF7DBwLs5Ar1xeNgg4sPR3LCoSgXn58": "LEEK", - "oraix39mVDGnusyjag97Tz5H8GvGriSZmhVvkvXRoc4": "LEE", - "3KAeVfDbU6tZxSD2kqz3Pz6B6f42CW3FdA89GUZ8fw23": "X", - "G2jXu2puUqyQsXmcL7SammGfkHX734wLZPce7yDepump": "LEGEND", - "J4ixzSEG99FWNoHRYfoaE3y2e7T7e7KX8NKNPy96pump": "LEGEND", - "LGNDeXXXaDDeRerwwHfUtPBNz5s6vrn1NMSt9hdaCwx": "LEGEND", - "5rSP1W2Jkir4Qy1M6rEYXFFi5HgbhrkVTajimrVgpump": "LEGIT", - "7obg932wg2A1oTZhrPzR4DSdhKbjXzKSfHTBo2Hu5EbP": "LEH", - "9EPYaNqzJLSPSFH9aKSxn2EaJoxHS1oqt7pVBR3yivt": "LEIA", - "7usVzynPTUJ9czdS96ezm9C6Z3hCsjb7j6TMKipURyyQ": "LEIA", - "2ru7VX6NnaZ78znCtgGmYs2PdcAQRCr3UaPfRkDUpump": "DUCK", - "Gc5hxBYZjxWNpt3B8XYbp4YoGCHSMfrJK7ex4GUTpump": "LENNY", - "AdwCEWQGzt3vuFMEPMf97AJMiq1eYL2sR7gk2x42pump": "LENS", - "8Cd7wXoPb5Yt9cUGtmHNqAEmpMDrhfcVqnGbLC48b8Qm": "LEO", - "Fnmq5udTPPkxGjw8nDtnRsjJWfHfdNmsfKGLhUerBAGS": "LEON", - "GsEbfbyjjnYZGMSkXhoxrqTyrzdYC5AFcCisS51JEGEE": "LEPER", - "C9smdbQEsoZhnNCYhqeLDNE17aMGdBjJvqzk59DCpump": "LEPRECOIN", - "LFG1ezantSY2LPX8jRz2qa31pPEhpwN9msFDzZw4T9Q": "LFG", - "HEZ6KcNNUKaWvUCBEe4BtfoeDHEHPkCHY9JaDNqrpump": "LETHE", - "G8Vy25NzjRmuQtnN35xF7j3X2Z1TrV39XijZu8Mg4w8n": "$COOK", - "7JKRJxWihe6Pt22L9vudAGaomD9ViXiZgXdk4v4kpump": "NOSE", - "CDBdbNqmrLu1PcgjrFG52yxg71QnFhBZcUE6PSFdbonk": "LETSBONK", - "hqYtLoxviwGENvogcJ5324YTbYqW5H9LHtmQu5Jbonk": "BONKAI", - "2vFYpCh2yJhHphft1Z4XHdafEhj6XksyhFyH9tvTdKqf": "LGTB", - "8SePknMizxUWZFnyHcNLXy6wiqEWUKG1kgV9Z6N7BS6a": "SENDIT", - "W2bAuFf2Xeb7ZNpJTywFSaCs5jYkaLYtBMR53SzVXUo": "STOP", - "AMEdarx3ryELQsFVNynCQdC6ABi94x8whxGjbmXBpump": "SINK", - "4fSWEw2wbYEUCcMtitzmeGUfqinoafXxkhqZrA9Gpump": "PIGEON", - "E4wg8YF472u8oiLvSNKB84Y23gZhkSQ3PSCJaUj4pump": "LEXICON", - "v12TwfofSbvVqQ5N5KGG4d3J8rtEi4BjGfn2apyondo": "LION", - "GwKKPsJdY5oWMJ8RReWLcvb82KzW6FKy2bKoYW7kHr16": "LLD", - "mntpN8z1d29f3MWhMD7VqZFpeYmbD88MgwS3Bkz8y7u": "LTAI", - "FLTHudk5B5zag7JmGXqrYrFfey6otevLQA6jm1UHHLEE": "FLTH", - "7Zt2KUh5mkpEpPGcNcFy51aGkh9Ycb5ELcqRH1n2GmAe": "LIBRA", - "Bo9jh3wsmcC2AjakLWzNmKJ3SgtZmXEcSaW7L2FAvUsU": "LIBRA", - "AKKasEPSAXaf9BRwyJTuU2TczYq6yUwj2FAk7U5b8cPZ": "LICK", - "DFhT16WmZjyUYaz2qwWWb7atC3w3yfDffz74cFFBUDAz": "LCKO", - "HZRCwxP2Vq9PCpPXooayhJ2bxTpo5xfpQrwB1svh332p": "LDO", - "7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj": "STSOL", - "5XqzzdodsNtAM8TtQyiqGVbD7GwLBBN7oVnRA3hLpump": "PILL", - "C4yDhKwkikpVGCQWD9BT2SJyHAtRFFnKPDM9Nyshpump": "LIFE", - "6D1hQ22C7oCUKKVuy91i8HTHnzSjsHCuNAcH9WH2pump": "QI", - "LFNTYraetVioAPnGJht4yNg2aUZFXR776cMeN9VMjXp": "LFNTY", - "node3SHFNF7h6N9jbztfVcXrZcvAJdns1xAV8CbYFLG": "LIGMA", - "qnko6WJGEwEU3JYQFZakLe9V8dmS4YAXFARHeRipump": "LIL", - "3meWn4683ng7FgLVJJB2bYPaNSoqhWdritNdkd4Aw1aj": "BUB", - "5UNr4EKg7rBwjKK9xBGxGVwHuYWXFWu8VkewKquTpump": "$LILLO", - "9vrGUHwsC8LyLjQoh3zJb9S53x7A88u49La63qPB6F5t": "LILPUMP", - "Co5WwGeZfpfqGE1tUyAp1SBjcmumoY9sNN99MXRbX98K": "LILY", - "5UAMZkfNmuVcKzr2wo8Jqw4R1k8vfdAVJNN6h3bVpump": "LIMBO", - "H3QMCaMh5LxtS9oGDwaMaRXPSPSiXVqnY4YsfrQMRjqD": "LIME", - "44o1iDPGuzHxQgDrPE3HRb2WXwGLsBsY7yMttX47pump": "LIMITLESS", - "86t88w3MKT38HChTBKBwEeb1RW1MeTceaW68qY2Vpump": "LMT", - "Edik9MoFp8LAXS9HNu2gRFyihwYqDqv4ZmNmVT9ondo": "LINON", - "XsSr8anD1hkvNMu8XQiVcmiaTP7XGvYu7Q58LdmtE8Z": "LINX", - "7DKFa79o6QfbGGEtBZ5ZYBJw1qLJWyNuVoMD5rxbpump": "LINGANG", - "GWZGj6AM4pkWxQW6bifE6JChSRf2hEQFWFkYC4REaL7H": "LINGO", - "6ty746K8QZFpGGrimYHMsAfXQnewszEgPfT39atbpump": "LINGYAN", - "25Ew9oMprcypdK2KbecFE6Btvy5HChaDYUmLdm7aLinK": "$NATION", - "9LrT8gAKJ5qUJA1wJoQRrVfapJGrNnU2ca5UYiJipump": "LHX", - "GF4mRrsd52LHBZ4Fj9ijEwHCnh7c2wTMwDKwZQqipump": "LINK", - "CV38eqqRKfhvqZ5yyuwuNFGTfof1wYvNguJXt2mZDRMj": "$BEAM", - "678qt3ZQCCBLjjzb5iC5FVmav94ayRiwsz3FUysrVyNc": "LICO", - "4wjPQJ6PrkC4dHhYghwJzGBVP78DkBzA2U3kHoFNBuhj": "LIQ", - "CKafriN2DxGwTWSBZJLhAe4JgyugadBg27SyL1ameow": "◯", - "J2kvsjCVGmKYH5nqo9X7VJGH2jpmKkNdzAaYUfKspump": "LIQUID", - "BoisYJqhMhgpD5291J6HRC3u1jeDkF8uSLyFy3fKb5o5": "LIQQ", - "2PrJoPoRzsm8DNuH6XPcTCtvt8XFzHBxqjwG5UC1pump": "PISS", - "BaaYhmQBjX3B6JAayG5mDbhziyKsf4KDr9ZHzaEhpump": "IQUID", - "72hdqAZq83HUYz9kxsswbS2r7NBGEnxtnugCSzi4coWY": "MERC", - "2Swx96wtZw9tWAqCLvPCnht3jmJ1SUQwy9FBbeHGuZvn": "LP", - "DDti34vnkrCehR8fih6dTGpPuc3w8tL4XQ4QLQhc3xPa": "LSD", - "3sfANVyJiq5uZCZvyizaDzBtHQQTNBr6RBGdXVmFcN5p": "LSSOL", - "LSTxxxnJzKDFSLr4dUkPcmCf5VyryEqzPLz5j4bpxFp": "LST", - "Cn5Ne1vmR9ctMGY9z5NC71A3NYFvopjXNyxYtfVYpump": "LISTEN", - "5z3iCe53hUANTiG8Js8RjHNE2Arjik7L2CXLyr2rpump": "LESTER", - "4Y47LEufvcSSSbTFojcvW4Y6x2KZXrqG2urNBSvHpump": "ME", - "DqRB2BZUfWFX8ZbCQvjWFor8KQTohaiTynSsYafbonk": "NOTHING", - "G2ajLzpiW6tPvRqdjz6QfFKyArrh7zh66Zi1rmoppump": "LITTLEBUCK", - "CS7LmjtuugEUWtFgfyto79nrksKigv7Fdcp9qPuigdLs": "MANYU", - "5eycqvLdZGfPUFcwsk6pzWJDkq9yAGYXxDP5jJTWpump": "SHITCOIN", - "FyB8VxxYAaVVchAgbB1kvjWdw26ovaD4ipwV1j8epump": "RUNNER", - "347k5f1WLRYe81roRcLBWDR6k3eCRunaqetQPW6pbonk": "LIZARD", - "36iztMbw5zshWLoyhdcNUZgsVBgEGzoF4NPuwk3rpump": "LIZAI", - "H74CYmXgMkYHYuSRsZt6RJb4NYp2u72Vw8BS5huApump": "LMAO!", - "FkdpsuYhtWnmLctmVki1ugKdxLj4nXco7XCLGcx1pump": "LMEOW", - "7kN5FQMD8ja4bzysEgc5FXmryKd6gCgjiWnhksjHCFb3": "LION", - "LoafdJ3WSAvsrx3zppSGKA6sRvL9GrRrU1iRV7HkLkm": "LOAFCAT", - "AVF9F4C4j8b1Kh4BmNHqybDaHgnZpJ7W7yLvL7hUpump": "LOBSTAR", - "75mwivXSRWm6sxUip91Zctc9m9H1NE48qDLcTgiXpump": "LOBSTONE", - "EoReHwUnGGekbXFHLj5rbCVKiwWqu32GrETMfw4ondo": "LMTON", - "8Ki8DpuWNxu9VsS3kQbarsCWMcFGWkzzA8pUPto9zBd5": "LOCKIN", - "CXBVSveFCtdih8mhVWtyrLcyPvpzNejCARHYhCzhLode": "LODE", - "HJUfqXoYjC653f2p33i84zdCC3jc4EuVnbruSe5kpump": "LOGOS", - "LoL1RDQiUfifC2BX28xaef6r2G8ES8SEzgrzThJemMv": "LOL", - "6fvys6PkSCgZcrDCCkYp56BjLMKZ41ySZK6qtgzX49Hg": "LOLA", - "AKyVUXwrYPxnt9cf9EQUpRmty6yrW25d3R8R1YVepump": "LOLA", - "Dt6vESocrRixuFz2taZunygpKcNQwxAjUMgYUxvNpump": "LOLCAT", - "53Xy4g1RJnGR6saaJRDNoo1rYTGZ3W5U321EDdSa5BGD": "LOL", - "LBTCgU4b3wsFKsPwBn1rRZDx5DoFutM6RPiEt1TPDsY": "LBTC", - "EGMuM8qhWTzTEa9P75DuT3G4DNVsexWww7fp1vo8pump": "LONER", - "gxNuJSHRScR7WkzQEYqvxVditaKkcc9V32KXPJLCbcT": "LONG", - "2HUDMBsnctpkQo2fdf6Z4ECrXhAJbemJ4NJr4X9Spump": "LONG", - "BCzBBz5c6fCcRU7UjdJxszaECh7FdnU551MoQewpQv7L": "LONG", - "CBW9tWMs2yF7oXLCZpRfGUjyqui5ppEKQ3NrQvjEpump": "LONG", - "CLEjjvLaGoQjGxu1N3iVvjaCxDVtEZv4YJ3pSoAYpump": "SHIRO", - "9yBr75m6TBZzQqHeh1ztfv454pmqAiAvHUHdpPQcpump": "LONGAI", - "AuYzLSUHCKiX4MTK21J2vDAqj6JX5Wa6BPSwugw7CoJA": "LONG", - "CyRbC97yrkUB31sv9PLw5Hw8276zXH4sRoTE1KcDpump": "LOOBY", - "9223LqDuoJXyhCtvi54DUQPGS8Xf29kUEQRr7Sfhmoon": "LOOK", - "BSqMUYb6ePwKsby85zrXaDa4SNf6AgZ9YfA2c4mZpump": "LOOK", - "8aZuUoqr9rBLjikz7kvpajm1JRqhoWiLMnrNtnRNpump": "UP", - "8ztixkSGb1sdq4cBAA44NRAdkUZBuRz9snquq72Gpump": "SENDIT", - "3Aa1AjmRw3MdAS3xXp2geGwsknd9s96CfSLzYLR5bonk": "SENDIT", - "sRCDYrKbg186hfdrVnY3K8WTeW13cj7M25sA1Kvx2f7": "LAY", - "CHX3FSxGYSJ2LHeQTcGp2oMAoBNngtJ73jsuamMUnZQx": "LOOPIN", - "GSSPA5zDNqpmao8BFnUHnnQrc3tgUeoVSesXtHhxpump": "LOOSE", - "44bzge9EZJGPJRYNmsA64mdKZ1eeLdDcDiczRmoyAtez": "LOS", - "BYMCXiiA4om347ChR4t6bYHzkKJwnULLAg72BeDdpump": "LORE", - "G2AbNxcyXV6QiXptMm6MuQPBDJYp9AVQHTdWAV1Wpump": "LORE", - "4riQPy1yXQ2d1FfzgLy9eJy6yoUdZuQkMj7JsVKr4bGu": "LOST", - "6KeQaJXFHczWKjrcXdMGKP773JKQmMWDXy4446adpump": "ATOM", - "5DQSDg6SGkbsbykq4mQstpcL4d5raEHc6rY7LgBwpump": "LOU", - "EJZJpNa4tDZ3kYdcRZgaAtaKm3fLJ5akmyPkCaKmfWvd": "LOUD", - "BprPz3BbG5dDqLoY1qYqnSadTEas54E5bi5MbyLAZr1x": "$LOUIE", - "7BMb4jNt2tQG81jX7W22H2h2UyL4SW9QJgz25HRhpump": "LOULOU", - "EEhPz661wF7t9DmEy3SufExspgj1DcxBt2FywvJnbonk": "LOVE", - "6LDF727vvVi5YzisQ5XRr5Mu38sr8qttMe6MbvdHpump": "LOVECOIN", - "4QQV4LQUUXAn1eN1XQGrfY65TfLe5STJcfsCQozqyb8T": "LOVE", - "4EzbvuUVUFBf3gvtPu5js7sfMaDW7WQVTXdYXoM7pump": "LOWCAP", - "edLdFJVVR532qhcrNTJjLAmhmyV7NsctbWVokMBondo": "LOWON", - "HKv9jA7VREs68BA3PZMREiwuQKzfLUjr9azgrzbNpump": "LQC", - "LYLikzBQtpa9ZgVrJsqYGQpR3cC1WMJrBHaXGrQmeta": "LOYAL", - "4SDfM29FFTmQDGKBHtThh9oYhdRcwPSTqYVgJ6EtyLSD": "LSD", - "Fof1DyVSYiQGCnT3uTbmq8kQMPdwL35x1bD82NaTs9mM": "SANTINO", - "5Ke381D44MEQg3BQarWhAwhj1xbdNvXe2KHkTiZzw7r5": "LUCI", - "qZtEkb7KAPDtgx8AzeMPWMvXAiYXto5dGbJGk9Zpump": "LUCID", - "Ak1StSUAardZ157jSQu4hMkkoPFiUowttuowUeompump": "LUCK", - "9aCHVRfLuKV6R5yBnuEjHQjs166yu6y2St8rp5pKpump": "LUCKYCOIN", - "Hg4sgaK1ppfYAokghanL825WtteecpdXsoaTbB7x6tqS": "LUCKY", - "F5qFr17LeunQk5ikRM9hseSi2bbZYXYRum8zaTegtrnd": "MODRIC", - "XsDmtC5EpfzJoCzDN2SJP98xpCsSR2CqdZauJSvHNXT": "LULUX", - "4pTN87RQi7Bt7yzU3CFk6FDiD8BYbH2dFYPwsAfjBAGS": "LULU", - "6Q5t5upWJwDocysAwR2zertE2EPxB3X1ek1HRoj4LUM": "LUMEN", - "6DoRX8vRqH68Nxz5ZnErndHdpAeQy9pDahaKqvaJ59Fv": "LUMI", - "BzpqoPc44oaHu9SB5hek1GRNjzc4UwFcL4oWusWozm3N": "CHILL", - "5Mp8mZuRpWn1t6MkZJJcpj2AyMS7kv9mvBecg6t33oH3": "LUA", - "4FkNq8RcCYg4ZGDWh14scJ7ej3m5vMjYTcWoJVkupump": "LUMO", - "HZb4hrdUoKCEDf1iB5pp6ohCpzJsv7tZ21JEGqHhpump": "LMR", - "9se6kma7LeGcQWyRBNcYzyxZPE3r9t9qWZ8SnjnN3jJ7": "LUNA", - "5DXrzEo4aGgtZRqDxFDcsp95hCakAJKi642gerppump": "SNAKE2025", - "F6v4wfAdJB8D8p77bMXZgYt8TDKsYxLYxH5AFhUkYx9W": "LUNC", - "WPgmcrani3ZybubqiZmMRP4XWGyUkMu8DeBTq8Kpump": "LUV", - "Gd4qRUCe3J871r5uVuxv8aUUnDbeT27TGaFjWVnhpump": "LSQ", - "BmXfbamFqrBzrqihr9hbSmEsfQUXMVaqshAjgvZupump": "LUX", - "3bSGpKYPut6RXDxp41nvb2GM7eR4kEmJExFFHoYkW3w2": "LUX", - "DHsUQZYRqZ5WNEHZxL5evnAt7A7W19BJ5GxUdJz3NcPh": "USAD", - "3hFEAFfPBgquhPcuQYJWufENYg9pjMDvgEEsv4jxpump": "LYN", - "HZG1RVn4zcRM7zEFEVGYPGoPzPAWAj2AAdvQivfmLYNK": "LYNK", - "8bd3PSBp15xbjjCJzEJDrQDPiBTXgAJtgoxTcmXxytWL": "MMX", - "M3M3pSFptfpZYnWNUgAbyWzKKgPo5d1eWmX6tbiSF2K": "M3M3", - "8UiPofjkbjqTqrymz4VY3wXxMcPjFuyq3Psofekymaca": "MACARON", - "FYK7uK4mobuk7EJ37hhmL7n3C5DRqGvrzFyyo1xkpump": "MDEL", - "CiYXBwHPrdNkMtxR8YEWKv78K6bQjFoEWhPQrZqEmubi": "AUDM", - "AnvacL8c4PDLyrQ92bbhfxMYtnckRja3dj6p6Yzepump": "MACRO", - "madHpjRn6bd8t78Rsy7NuSuNwWa2HU8ByPobZprHbHv": "MAD", - "Fa7ZE9nCEYnrHsnoeHuhEExJpchtrBtKXnWe6CgHpump": "$MAD", - "Dy7M5B3Z5GnyhyHKkcHRFpYxw6eyiF1gqsDTBiT4t4oQ": "MIA", - "8H1qfZSZjmfCeRvh1AdUX5LqXw6Wuem1KCWUf8t4wivu": "MAD", - "98i1pJRAy4yWbHcjX5mwgBUNsX7XskvYWrBBrXgN8QLU": "MLSTRAT", - "TrumptpNNBEgVjDc8bnemRTNYZKhdsst9ujNAnTSHqp": "MAGA", - "D4n99BqHGvdL3ExyvdETV6Y4rWXkf2WwRxhaV1Epump": "ATLAS", - "8igRQGU3C1dAMrgc5qJWbZr8Bo7zxkucQvq2eFkgZMMW": "MAGADOGE", - "A6rSPi9JmJgVkW6BatsA6MjFYLseizPM2Fnt92coFjf4": "MAGAIBA", - "A2ZbCHUEiHgSwFJ9EqgdYrFF255RQpAZP2xEC62fpump": "MAGAL", - "2Xn4cBcrksaBMMxw8EbwuxY7iG8HUiZx1XEiCXQsGeSo": "MAGAPEPE", - "2ZRsj42U3Pt5dSfKbizp3HVygCEsLijwYBepKUMqvCxW": "MAGASHIB", - "6dk9H7RD5m8JtRuUErNztwhBbr5ynzWvzPTusLpxpump": "MVRS", - "55qMv1HtV8fqRjnFwDb9yDi9tBCeV8xwfgrPKgiJk5DN": "MVP", - "MEFNBXixkEbait3xn9bkm8WsJzXtVsaJEn4c8Sam21u": "ME", - "G33s1LiUADEBLzN5jL6ocSXqrT2wsUq9W6nZ8o4k1b4L": "MIM", - "M1M6sdffCs3ozzhpRveweRCWdZhxth4mvVujPtYEC3h": "MIM", - "2TK6MgLTmHDhmdh4EtdnBkYKZPkcjMp6cj3gcWhqpump": "MMC", - "GjYWU3CfPKzURV4B8vGMdpGkNd5PKiwARYizJjeLc3LF": "MUG", - "8iWsK2WH3AGviQwAnt43zvc8yLy6QMUSuv8PK2A7pump": "MAGNET", - "3im75zkgSaSwnEBEe4DiCSKUkKGKbWADyGEW4teZEmha": "$🧲6900", - "HPm64oG8eoCKuPj2MaHc1ruqdZ4Pe71UEGiUET1MtJAu": "MAG", - "Gi2ce3Eu6dt8iAS2pa8hLsoudSEvew477SVJkHUipump": "MAG", - "G5e2XonmccmdKc98g3eNQe5oBYGw9m8xdMUvVtcZpump": "MAIAR", - "HoCunqcWb8b3PtuX8aPvvF3R2nCmfjmTj32zSgSvpump": "MAIN", - "E4pa6gJTn9DTjmyDRF5rAXGG9YbXFCCUFZFxQCimpump": "MCEN", - "CNyMaRR4krm1yAxfCvPXevNMArZZZk7sRqKwX68uCeJk": "MAJOR", - "APXEzWaC12YAejZr1v4sBhTcJMosGSA6oV5aTn9Jpump": "BASED", - "DdPkpeRXdKLfZkR3iYmbTG41eCRrgjfagw4e4o7kpump": "MAMA", - "5mfWKJrjVtpZo8EvwbytFWH5X6T8YecxrrYCgskjpump": "MEGA", - "ACXK4KmfXrf93e3AEo1ZiGDDDpcBpNEnWVxy9BHFpump": "MHGA", - "87B6mb9KBjaF5NHrB3H33f7grdUHi4oWmMErjhZ5bonk": "MMGA", - "jkAWzPumRhJE2KyqVy4kB7m9KwXBeSEXnFAGoWtpump": "CLAUDE", - "2Yb3VToPpKvFuNLqoLeVVvTt4o2dpjdd8TUqaXjDpump": "MPGA", - "BAmHS42JqR51sZ6oqPhj4pcqi6dFFYDka77P4Q22pump": "MALAKAI", - "SJWKUQ8hcSjxizfVGmAB9Rb97znrXFHbRiad2J1pump": "MAMA", - "AG5iXv6pFuQtszqcVzZatTiH3EM5LvUhm6WnP42Gpump": "MAMBO", - "25hAyBQfoDhfWx9ay6rarbgvWGwDdNqcHsXS3jQ3mTDJ": "MANEKI", - "147uk4CFxsppdmKZUAakfLvduo4x459HiJ5Ct6MzJory": "MGN", - "MangoCzJ36AjZyKwVj3VnYU4GTonjfVEnJmvvWaxLac": "MNGO", - "6cvrZWgEUkr82yKAmxp5cQu7wgYYBPULf16EUBp4pump": "MANIFEST", - "13TVvChoA7S2qdB7GhP8sDret3aPBetBjvWHtL3Ymeme": "TIME", - "EWzC9XK43NaEZ7wA3L9vyucKy9BDjzRR9WTrQ78zEgjU": "$MUSA", - "3srdiFxca22oK1g7qdbGGe9qXy4aS9QGVdTETCBFpump": "MANSION", - "1xdtu7y3LkkrVCAbm5KGKfYzq1qgKhxxk5AaJBqpump": "MNSRY", - "JBYXZaWcgdWSGvdYXior1hYyygm3eMYiPdd7KiWcpump": "MANSUKI", - "Mant1sZcb8x2YMZe7RdqSfStCj4YxjmQByNKyHpLJK9": "M", - "BsTRFEVZhXbBuy5fhxbttuim8iwzXqMdRCfFftDAkkeu": "MAO", - "Fh4GcNpEhZJvDDdXhUCe7jxzhqQKZk2NGgDY9zjBbonk": "MAOW", - "8i9Ak4wieisvyYvk92S3tDGhZLjG3AzRdq4saUSWpump": "MAPPIN", - "MAPS41MDahZ9QdKXhVa4dWB9RuyfV4XqhyAZ8XcYepb": "MAPS", - "ETCJUmuhs5aY62xgEVWCZ5JR8KPdeXUaJz3LuC5ondo": "MARAON", - "XsguBZPkM9BDmxspmWe29EmrYZBv21ENcC27Pqh7grB": "MARAX", - "7u9kr8ZCHnkeS2n46jVuyq6tUegAtaCUM52AQ1iARKP3": "MARIE", - "MNDEFzGvMt87ueuHvVU9VcTqsAP5b3fTGPsHuuPA5ey": "MNDE", - "FprQaYuyA4CN1f7UtLd7Vd1NR9vyACBy91cE8tiGpump": "MARIO", - "HTJjDuxxnxHGoKTiTYLMFQ59gFjSBS3bXiCWJML6bonk": "MCAP", - "C4j7kPx9PqDnfvxe2uycJQRTAeyGwmU4DyGf21Xgpump": "MD", - "jDjBkYnLT7ruUziEkwWKYy9hVUjLtfkf7dY3PMarkEt": "MARKET", - "6QSVGUEyBZWRshnXKhS96NQ97vGWiTu61SyHLAbYpump": "KACY", - "3CnqgfQmiCyjMgaDfJpBDi9mjsE11vFnTfniM8BTMBzK": "MARS", - "EqMuraVKeuEUFsDfRJuqNbyibcDNkYCaBMrzZJJNKAQU": "MARS", - "Cfmo6asAsZFx6GGQvAt4Ajxn8hN6vgWGpaSrjQKRpump": "MARS", - "2TobP67e8TXkGAqcSQpu9WJPeBnKKZxAnHN518qxbonk": "MARSE", - "GqXX9MfkURBZ5cFym9HDzqTL7uZkjtCSqLkUSe2xpump": "MARSMI", - "maru4DtHRrPytGBgRk81qBLby8f72t23u4Xi3EgPPgJ": "MARU", - "FovBwhoV5KQjZCdhoM6jgXYwXLX3F8vgAfvmLH7ondo": "MRVLON", - "XsuxRGDzbLjnJ72v74b7p9VY6N66uYgTCyfwwRjVCJA": "MRVLX", - "GmcjrrHrJ9v2dAYzPYVFBm2CrwLbX2ZvYzJC8Khg9E13": "MARVIN", - "Gtas1jTXjD4EpYqDFKk7cEaArFSdaUXKu6bGhbSvpump": "RUG", - "7M3mfsJ4eQAwRt3WwfWUccTPGCRxQauFqBULEDVepump": "MASK", - "R3ywbVQ5t8LNmjQsn2Ngv43dSqyZscQwNag9G3Eondo": "MTZON", - "63bpnCja1pGB2HSazkS8FAPAUkYgcXoDwYHfvZZveBot": "$BOT", - "EsVHcyRxXFJCLMiuYLWhoDygrNe1BJGpYeZ17X7ondo": "MAON", - "XsApJFV9MAktqnAc6jqzsHVujxkGm9xcSUffaBoYLKC": "MAX", - "5npn63u1pATvRxevzUsDTzJ9yChVxTKXHBC1KXfStoy": "$MATCHY", - "MAtesZXcU38mgPuteRFo9ApCauYRRuwn3xf1cs5y7ZX": "MATES", - "CaGa7pddFXS65Gznqwp42kBhkJQdceoFVT7AQYo8Jr8Q": "MATH", - "Gz7VkD4MacbEB6yC5XD3HcumEiYx2EtDYYrfikGsvopG": "MATICPO", - "6gJ8ZmypNAJmE3uQPgM4Xi94H5aYtDLZRoMGFSbw39jQ": "MATRIX", - "ALQ9KMWjFmxVbew3vMkJj3ypbAKuorSgGst6svCHEe2z": "MDF", - "E1R4RF89GcKxz62DVfojxDJteLFFs8rtiXcGcrx5HbTj": "MATRIX", - "DEJqUhPTarcaNqhT7c6fktUszcJWN6skqyWSxXchpJNm": "MATTLE", - "oraim8c9d1nkfuQk9EzGYEUGxqL3MHQYndRw1huVo5h": "MAX", - "Ajm9zwikHFeJqKpuDmji7dvyKS4PL1CJDPvLx4Q1pump": "MAXI", - "HUPfpnsaJtJGpJxAPNX1vXah7BgYiQYt1c2JMgMumvPs": "MPST", - "H84qihes12nVQarr8rzmw87hDXUbHtFKRm5joBcbpump": "MAXWELL", - "F5hXpxtsK7UeFC2gsbiTbyhVrUNXN5JG7xkDqpQfpump": "MAXX", - "32CdQdBUxbCsLy5AUHWmyidfwhgGUr9N573NBUrDpump": "MAXXING", - "25x9Av7ddPJQFVJy512VzzQ96A2Tqza22LtfHTKCpump": "MAYA", - "81uWcu3yN3VJmsnXnfEpbTZ5T9EZg9aJCRsnTMER72NE": "MAYHEM", - "8R47CZYmHbwXMnsVLXzTCnyLyR8iofkN5WxFgmnE3NHT": "MCBROKEN", - "EUbJjmDt8JA222M91bVLZs211siZ2jzbFArH9N3ondo": "MCDON", - "XsqE9cRRpzxcGKDXj1BJ7Xmg4GRhZoyY1KpmGSxAWT2": "MCDX", - "Buoj8HCZMnLRwzDmjzaswhkVhLZD58PG4pZ7rnYp6pCr": "MCDULL", - "CdmKJqhHEkqTr1BFBdcoBNHQvh2BEqLap6ivt2b2pump": "MCFL", - "6m1Au79gHDxBztJbne1yhj9QzUT3FMzzz7AK9fw6is5U": "MCP", - "MEANeD3XDdUmNMsRGjASkSWdC8prLYsoRJ61pPeHctD": "MEAN", - "mecySk7eSawDNfAXvW3CquhLyxyKaXExFXgUUbEZE1T": "MEA", - "9MT9TEqxJQcSYjzBhXJzQvQa7rvjPLN7FyamYvZpump": "MECHAZILLA", - "ETAtLmCmsoiEEKfNrHKJ2kYy3MoABhU6NQvpSfij5tDs": "MEDIA", - "8H2WoidBArpZGK81KMrTGNqnE8gT2CxdvwUETvUm1Ha3": "MEDI", - "5RyT8Yf32dbKMLqyKqTwv9zBHRz2cmgXhVCFj3jWpump": "MEDJED", - "XsDgw22qRLTv5Uwuzn6T63cW69exG41T6gwQhEK22u2": "MDTX", - "Fosp9yoXQBdx8YqyURZePYzgpCnxp9XsfnQq69DRvvU4": "MEDUSA", - "8xbMY4atJXBNKR8Kk3u9y5XztXMd56uS8TXY9Gx5RwMJ": "MEDUSA", - "WppXGfc63fwKSNpK42xVRxxV8dRBtHKRE6YQPhMpump": "MEE", - "GEdBv2DnES89DvasmZ35TaxP9kBibznYKbacXpoGTEBU": "MPIX", - "9WAdURmxA9Xr2HwdwkKbAnkv368eTgxMvwnD7XDnzyk8": "MEGUSTA", - "jimmMDpC9VDEeggKkgNwdMVDNhTqcbQ5sMG8ZoFJ4b4": "JIMMY", - "FUAfBo2jgks6gB4Z4LfZkqSZgzNucisEHqnNebaRxM1P": "MELANIA", - "7DGJnYfJrYiP5CKBx6wpbu8F5Ya1swdFoesuCrAKCzZc": "MELON", - "83iBDw3ZpxqJ3pEzrbttr9fGA57tttehDAxoFyR1moon": "MEMDEX", - "Bw5K8eZaf361uDLHgX2UUn1PNfC7XtgQVvY9sSappump": "MANA", - "ChanGGuDHboPswpTmKDfsTVGQL96VHhmvpwrE4UjWssd": "CHAN", - "BVvywU1wt4FaLDfZe7y11yrVnHbJpAU7YirmsuW3pump": "MOIN", - "5HV956n7UQT1XdJzv43fHPocest5YAmi9ipsuiJx7zt7": "MEMECOIN", - "8W4qpyLx74vwBRewa3rVEPPVMnJ8VWMkCTWCTSYPQTDu": "$MEMES", - "GRFK7sv4KhkMzJ7BXDUBy4PLyZVBeXuW1FeaT6Mnpump": "$RICH", - "5gcMBTPeogu1d9M6jjxWdHkHSgQyWsxWMBjpnf6bpump": "$MESSA", - "BPhtSt3fXqL31hhwbX52iuhR9vSjoLFPVcMQCXfCUhXs": "MEMECYCLE", - "4ep5qigDe3DSSzZjuMKe8HcX4ohacS4H1Lksy122pump": "MEF", - "AgDNMAi8r2QS1FQEeTHLsZSmkQKCK7xXP2bR7jQ2pump": "WTF", - "CAY4vG5i4kJNs9i28sjHf2shG96si3r6yusFQxfXFray": "MHIVE", - "34VWJ7PPwcPpYEqTGJQXo8qaMJYoP8VKuBGHPG3ypump": "MEMELESS", - "71mfKdePwyWXtiF1mqu2aaCdMKnKuN664z2vEM2Xpump": "LIQUID", - "HfZiQSCUTpvWa9Mk3FKieQHzZfiduPqTEMVDbP1Gpump": "MM", - "6CfnWgNKqJWWTTZsU2GswHNNKyXR4VfjygUZ73kh3U1L": "MFUN", - "598HSJPxc2dwH3V3Gxm6dhBL1te8NzsgA81quqezpump": "MEMEMIND", - "5EpbKX221NYVidK6A2nJGhtuLPvrPiQ6shknLbtjBAGS": "MEMEPUTER", - "5aYpWBtJpdiNFMcitxsysdFuxfMK8nRMePvVfkZYbonk": "MEMEROT", - "8D9foi1nqfabp8D3uNJCrzt1xkvxq8xHYH8Lxb4fbonk": "MEMESAI", - "Ccgfe34o6auHHBGgtZp2RRcScxxJgbEhQUAzNieE1cX7": "MMIP", - "6kjzL6NBtwhzdhZJASqHMimfaxNBGqKd2h6JHnnbRuao": "MEMES", - "8wvLsACsR3owhGzmLLHTgh2waW2vKNtVYWDs9cCopump": "MEMESTOCK", - "HwVWzgGTL7gUbdawENAaknUnoHZnAyCj94G672dppump": "MTF", - "EoRe4xECTe9imaYtwfdmrGoinD2S1N5yMeu1LrjQpump": "MWOR", - "AsjP9VyKUSuLSeycoTb9AqGs3PH6BWAvqDoKmxrWpump": "MEMEXSOL", - "8Rqc8foPzUZkxRQGuyCTAZS7QKAwncJVvgKXz1AKpump": "MENG", - "4oz9DZabbYNeB7UGUA4HYZKYRb4Hwm3h2u2x4CrumbAr": "SPICE", - "qRUZaCpgxaRH1s5V6opjPA6Hnpv5BM37LqkDBw7pump": "MVG", - "BUhS5coXEt9hcxN3JSpGYUWSKbNo96RsKu52LcMo12rf": "MEOW", - "dmdrY5iZhmZNGVXsPqAXuAXJ2nP3U8LCfBHA8MGpump": "MEOW", - "AcJTj14wb9VjVtVrR7E7eoxeK4HayqZ9bPdNvkiEpump": "MEOW", - "CRQdQmb9TDmG9FFTPEL9gqDvfyF6HxGaHwiq5eybpump": "MSPC", - "EWwdgGshGngcMpDV34pWZRSu5bkAuiKuKTTHKQ8ondo": "MELION", - "bn1fb8dwzafGePqNPrM8m8cbAKQiFqeEPuZkPySondo": "MRKON", - "XsnQnU7AdbRZYe2akqqpibDdXjkieGFfSkbkjX1Sd1X": "MRKX", - "7X9H2VvwRMJqLBUwZEN9CMptFESdyJ7Fp2SjLyNDpump": "MC", - "8HxW9Z3YT1gXgQcgEynZaHsEpLXRgijGfMYCkaAypump": "MEC", - "SUhzKcEZdShM9ifMb7kDKwAdjy6R9MogwG4fWiZtime": "MERT", - "METADDFL6wWMWEoKTFJwcThTbUmtarRJZjRpzUvkxhr": "META", - "METAwkXcqyXKy1AtsSgJ8JiUHwGCafnZL38n3vYmeta": "META", - "FrkF4GNdqKZLuzWXTk1imqMJRGK9uxCRE3PaBKZbpump": "DRIP", - "7gcoey4EXJcZ8u3iGYhgTBrh3JuhLWzV4Gs1zNaPtu3U": "MEFAI", - "G8vP2rt8Q16yVD2iJvqRve3mQjE7qA1T1EZCD3htpump": "MKT", - "3oqcUejEoAjGKcqBRs98XRmB4grsBk2rjjPZS7wEbonk": "METAL", - "CrFBNi3GHQ8boqivWUTbdwd1a3xwSMfkaxPzkocipump": "METAL", - "H1nf1F4eoHtiWiSBwLYAhJvAb7CDFY32eYA64mhL8Rp": "MCG", - "8SQkCjdA7E4xVyF1KTWcYJ727szoZGS9hyuHigLkpump": "MTLR", - "fDxs5y12E7x7jBwCKBXGqt71uJmCWsAQ3Srkte6ondo": "METAON", - "METAewgxyPbgwsseH8T16a39CQ5VyVxZi9zXiDPY18m": "MPLX", - "HCgvbV9Qcf9TVGPGKMGbVEj8WwwVD6HhTt5E2i3qkeN9": "METAV", - "Xsa62P5mvPszXL1krVUnU5ar38bBSVcWAB6fmPCo5Zu": "METAX", - "METvsvVRapdj9cFLzq4Tr43xK4tAjQfwX76z3n6mWQL": "MET", - "7w9gX1WBAYs6KnTd5oZdNNDS887YcBo4yScj66dUpump": "MET", - "Akzhy1LzbUzXVrmHtoiht1tyRVf73RdtRLFPX6aspump": "METHOD", - "9bxaVJmUwSc71j8Z2pvUL3UAr1s5fCnwUpvYhqV9jtmw": "MEWING", - "FyPDfX92B4uEk4zZouy96d1Kk1LgnCznBpzAFSsZpump": "MIA", - "HSqifUk8nHKcrwVSxt4urYngDAZesaiAxRHTtr9bG8W4": "MICDROP", - "AywAYdNJnSLSXwKWYxDciPjqGRnwp4iZdQptuuQTpump": "MICHI", - "5mbK36SZ7J19An8jFochhQS4of8g6BwUjbeCSxBSoWdp": "$MICHI", - "Ebd7KhyvViXd13ktu9tPnusdvJHojUayeBNT3ZFybonk": "MICROCAP", - "qi2bq4ACHHk2d77XaNx1xUNC3axcQSCDYcpeZxZpump": "MCG", - "Fz9edBpaURPPzpKVRR1A8PENYDEgHqwx5D5th28ondo": "MUON", - "XsQLZycSZ7QnBBdBXQaTbQdiUcbRqjNJgyBGAMzhHav": "MUX", - "FRmH6iRkMr33DLG6zVLR7EM4LojBFAuq6NtFzG6ondo": "MSFTON", - "XspzcW1PRtgf6Wj92HCiZdjzKCyFekVD8P5Ueh3dRMX": "MSFTX", - "FSz4ouiqXpHuGPcpacZfTzbMjScoj5FfzHkiyu2ondo": "MSTRON", - "XsP7xzNPvEHS1m6qfanPUGjNmdnmsLKEoNAnHjdxxyZ": "MSTRX", - "J7CSUzsM2VfBUaeaer5aGVVLzzUoizBWF1vQX2NLpump": "MICROWAVED", - "72Nb8vtM6KPhiidZBscKVzzyUrUckM83B4ChW6Vpump": "MIGHTFLY", - "2eCVVZ4tomqn4eyuA9Gh5PSKrjNXGwgMhPALGtAkpump": "$MIHARU", - "55WZGGCwE66xnCxBcAxKSNT49wzeaHhT9jE2xWSXpump": "MIKA", - "5Jng6jkLKU1o8BNrCzTEMXMFvPjNJZTpdWR3Hq4RHJb6": "SHIKOKU", - "8CZf9HMSEfDpsjgovrj9xDgikPJ2XRMtGG6L4sLUpump": "MIKKI_OS", - "6Tk3uHQpLDtd4Qob5fduLjfhXxAasWgvdo8hYeswxMiL": "MIL", - "Byb8WojwPWthyMm8iwtcd9CQhcZjnjQmhTRi5GN7BAGS": "MILADY.AI", - "3X8GcLiH2HttjyqePg7MazpMbwbgq5URUMTyDz5tkmdE": "LADYF", - "43N5UGr3mnfhJFzpBPNM7ZdnobghiYBtHTaQfZQv65mh": "MILEI", - "2ubuHGFS4VJVxSEpvV3kDwz6JiuXdaAoGMwrwYC87tp8": "MILKBAG", - "Ax8PSfCXxmxb8C8kYTzN5CPpTe6PyeZfFf8rrXNCjupx": "MM", - "3tzhVtTYHmqSuzLfne4S8YX6cpyaCCsWXW14XxESpump": "MAD", - "5nDSCvBxXY7hEsAbpjJr51WaLDDKgmFjNWAc6wD2pump": "MOON", - "3Rcc6tMyS7ZEa29dxV4g3J5StorS9J1dn98gd42pZTk1": "MIMANY", - "AXyd5cZqJDYRoN16xcv8s6k41M2kJMfKJnQiXeMd1rCg": "MIND", - "AJofCoVif3wj2Uy7mpzgxbqDyHPyn7xp6WzJBy7gpump": "MINDLESS", - "2eTJqpK4QqWofGLSoirWNdC3Goyxp81KC3JDMqcupump": "MINDSHARE", - "9VYUuSVZGw82Nz8rcSix4x4zurBmvkfiKtyvYCY4pump": "MINDVOID COIN", - "zgQnq6GEUWuEEa2QvqT69amJtKaj7oU4nKDP4cTpump": "MINDWORMS", - "27265MRQFxz2LSTnn4mNTmcz1HJsjJVBST1cozxzpump": "MIND", - "axUxN2q4AWzHaU6LXmjqQh7KEjaXDPKScjmzwEBpump": "GRANDMA", - "42yzLyxGDjD5RFFQFYNVy7Pzubz6QPCK7HFK52f1pump": "MTARD", - "9uu3yAY9uAa76pxjAuFe8BPKEQ56CZGJnaxstgW1pump": "MING", - "JCe4cq9bqL1238wfFjfFQ316PrS2sASKkesLYEyCpump": "MINIDOGE", - "8J6CexwfJ8CSzn2DgWhzQe1NHd2hK9DKX59FCNNMo2hu": "MINIDOGE", - "DSXVmrBySfBcmdNDGQkk59hGwXhAKNjEwc4as8nfmysd": "MINIDOGE", - "2JcXacFwt9mVAwBQ5nZkYwCyXQkRcdsYrDXn6hj22SbP": "MINI", - "9VucGkAroxyVJzuVadJy2ErhxXxAnPCpyWhong2fSYdw": "MINT", - "AvoecWraqX969kfXUF5XCCDz59sjRjiDa1KUDyj225t8": "MINT", - "minty8wwFx6AsYcDHXZXnWG5kcf8VskJHhsJrDeLuCb": "MINTYGIRL", - "RufNMBEbKJr6CM1t3nwakZ3BaE9csF2AR15s32VWcRE": "MIRA", - "GBUxQFRXQjSPjkxymAUKPfbUbSpRY8Ui7az1HCxtpump": "MIR", - "AbVVstroW1r3XZ2t4YbUYyBYyfpuYCr6d2kaYQnXDWhG": "MS", - "FjTJCCQpLU4fpH58mN1bTQXiQsjJVYai3QYFjYqYpump": "霞", - "FDEF2U9geiWS7sdGPCUo2r1wGswkJr2mmVTECiH6pump": "IDRAWLINE", - "4s4H5v4TXpmS4Ss66nxcCLgxrU5nunuwtkQceinZfGuw": "XIN", - "H53UGEyBrB9easo9ego8yYk7o4Zq1G5cCtkxD3E3hZav": "MXM", - "CdqUsAfihtKntBsXPrFaovYyr642zKmZmLxzwWQfsoar": "MIXOOR", - "964ssiZnVnZJrjCDvCbBuwgsozW13gmGtJdPWTAwpump": "MIYA", - "2jPaomSE2eNsqX9skmeAtRVWSQ3XrV6xBBcDuwzApump": "MIZUFI", - "9XS6ayT8aCaoH7tDmTgNyEXRLeVpgyHKtZk5xTXpump": "MIZUKI", - "B1Ycj8Bqv5fjoNZJkzAvNiis4FVwTVeGWvrLd8ABEqT9": "MLMX", - "6QhZ6WQyYjLGDXFuc9CP1MzzvfeA6rDVnPYUke6ybuff": "MMO", - "nuumnQ637GtKPis16AeU3PhNru74S7tK9GdjYVATbxL": "NUUM", - "7NQSHjuEGENZDWfSvPZz7oP2D6c5Jc3LjFC6uh179ufr": "MOAI", - "2GbE1pq8GiwpHhdGWKUBLXJfBKvKLoNWe1E4KPtbED2M": "MOAI", - "Cy1GS2FqefgaMbi45UunrUzin1rfEmTUYnomddzBpump": "MOBY", - "EVDQN4P1YTTD1Y3eLBDBoALWnR4aMBC9y2xUYCrbGbuy": "MOCHICAT", - "4ky1REfghVawQ6EPZGdx7zss5KNKStwE8swjii3uU5sD": "MOCHI", - "JxxWsvm9jHt4ah7DT9NuLyVLYZcZLUdPD93PcPQ71Ka": "MOCKJUP", - "14VP7DvCAdBCc5XGNZkPt6zhtPzJrWWS64Koxtxyondo": "MRNAON", - "BsoTnJbaYECP5LX4oFuJnNHBxUxoS9usjTC7fSaebonk": "TRADE", - "2obLASom28dxTDLJxfocejSD5emSzbESWmXuX4Fubonk": "PANDA", - "8xzoj8mVmXtBQm6d2euJGFPvQ2QsTV5R8cdexi2qpump": "MOE", - "C6Q5fMpuPjBPox84wbcE3Rn8HYJg11o4YHEmbQSuYs5L": "MOEW", - "BR4qrJbhGMoGSaF7QtUMvSZRtWhzvMUk5dkgkRrnrn8A": "MOG", - "GKvFocz56H58xVv2Yh6hHdp3sD5NdQST7NsnMMjC6A8q": "MOG", - "5oq4zKetRkUMMrFtkWH7r1Q6HZJMsTjgCeU6isgYpump": "MOGGING", - "HJwToCxFFmtnYGZMQa7rZwHAMG2evdbdXAbbQr1Jpump": "MOJ", - "Eiy4a5QjY6PsDTcjFnoJTFa3noTCn1xFVXN8BzFNpump": "MOLECULAR", - "3QPiinFaKeBkv28boLjn9rv1ci6WEd6VXAfmNHz8XEeJ": "MOLECULE", - "GnkitxfvNLGGsXKGckU2Bw9uEnzwmVmJKzTaHpp1pump": "MOLTIC", - "DgN9zcmQgWF98qkhoMEshq7G4eRDjxcHoXvijfffBAGS": "TOWN", - "HYhnRdn7nridbDUUcRsd3JtmvMCyKrVDew52CWGtpump": "CRABS", - "moLtguTf84g34S34PbnJDRKR1FXb5di1UCRsnB7hKjE": "MOLTID", - "HndwegC6q7UGn5MErjvdH6BeQzcWQtjZf1nJX6rhpump": "MDEX", - "CHVggq5Bu2UKLB1MQtLciDV3UKv5hwLkJwA14egApump": "MOMO", - "G4zwEA9NSd3nMBbEj31MMPq2853Brx2oGsKzex3ebonk": "MOMO", - "6VHXipL9pSV5MCYuB9tMR4xkwojhc1gQ51ku2GR7pump": "MOMO", - "9JbxQSKNukRA7cPZxCfhSNEcAP9iKRo3PSyYNbW4pump": "MOMO", - "HcQbgvMTCNDgMDY6GAFzP1WwCNZeMoEwZfUuHepzC9L5": "MTOS", - "3zomtMhRRsBpLZeJLS86fgueXE7xAhoN8dnymNh8pump": "MOMO-CHAN", - "DycqsssTijnk7AzAyPj5nJuLHW42PXd2NDEJGW77pump": "MOMO", - "766whddpsQHS8wy7viUfmuGPrMri1kgGMFwfZMdepump": "MONA", - "MoNAoCoXKLb38Y2VpbukGacdNYhvRTyVn92DvRApXep": "MONA", - "Bt9tUAuuoJTihrLvgK1kWErBJduaGEeQbF2VhAPHtYm4": "MONA", - "3Wp5z1GtPqKwyiaicXa7nyXhBVJJ5JgAwzWXuPXqpump": "MONDO", - "4bvgPRkTMnqRuHxFpCJQ4YpQj6i7cJkYehMjM2qNpump": "MGO", - "2MpjiarUbQnJaWbnypSaeoA3ks58Qv4YamXjtKBxbonk": "MONEY", - "Gh5uaxSHkdvRpmuYrZDkXEeNCYcWKdkVDsWYjx5ae9Am": "MZC", - "FsBPYiGZ4bhUxVSPP7XPJYfTPm5PsLJc2WGZaFaDpump": "MONGY", - "G8dUSvywefr4GvfFZBZiLHmbjnwjrrJPAnVifjj7pump": "MONITOR", - "9EQSeWY7pDB7MYoSFr8QJ19onGS1ehDmLbSGT2b3pump": "MONKE", - "hxcxN81ma8m5PMzPuCyysaMJ9wqJenBr811DbX4moon": "MONKEPHONE", - "92d5AncUVELq79xqLWNQ5Zoxz7fn9XHwijYswYWFrKtR": "MONKEY", - "921MoB1U7VprQfWw5D37a38LCBgB3nareT7rNffk66BG": "MONKEY", - "6cnDfXAG9EsGn5BB1QF3i6Xx1SNJ88Qcc1nckJp6pump": "MONKEYAI", - "Fm9rHUTF5v3hwMLbStjZXqNBBoZyGriQaFM6sTFz3K8A": "MBS", - "HyLXondhNhGkvWPXr8z4EPSgZNRRs4j8BHdyqMdVpump": "MOLT", - "5jPAVBTrCVWNy2Lh9wtyV6y6XMvD4UAgADjEnMp8BAGS": "LEAF", - "FYa25XnBsXQXAdTnsyKBKd5gZ1VZhChBRF57CqfRxJZX": "MONK", - "mpoxP5wyoR3eRW8L9bZjGPFtCsmX8WcqU5BHxFW1xkn": "POX", - "8HWCntWrLE4GJ6iK5cwvbhvuQSoRCiu3QB5jHyqLpump": "PEDRO", - "7TSCoke2mSZzAtyuRmzANf9virrnyv4xSUeaxUrKkLqw": "MONKEYS", - "E3SPN9aQjT6yeCsQou5wKb5iaG68CbUE8ZnBaFBpump": "MSI", - "AMgMqadZ7Qpe9PCQwehAG3Xbg5cf8JxCxH7sGCSupump": "JORGIE", - "CNNQZyEWfz9mDBRCiRNwjaUvMaLnaRWem8HeJYh7bonk": "MONKEY", - "XsyusqQvb8RDULsY9szwvUv2CxrDKa642w7kecZPdRM": "MNSTX", - "FuLH3f9TPxiLNojPBZxfaT5Rb5VFfia8hMzLo9e9c6CG": "MOOCAT", - "8eKeugXxvTgowqQZWfxuWJJjKKYe9vcZ92GBu94spump": "MOOCH", - "ED5nyyWEzpPPiWimP8vYm7sD7TD3LAt3Q3gRTWHzPJBY": "MOODENG", - "He8S8wC7KQUH7qGWAaLRNFpjqrVyQBwUud34Eyj82AZo": "MOOMOO", - "JJ4QpqUYzs9LUPrF6uJKQbkA4ofSnvkwQ3RBcVLFSjd": "MOO", - "3ZvUPAegNgfWnTRfAhhkZkn4jn3XERxvSW3uQetmpump": "MOON", - "aDJLHm1KxsHmnoYJY8VDyWvbGykBNhWJqNgPX7Ppump": "MOONA", - "Fdxex2V4Ldun1aFhPsuGKfzPrrE1ay4UM8xt92EaL5N4": "APP", - "B9jYrBoCPN7FcXVHe56KXsYgJE5gbTxsadjvyFiVpump": "BAG", - "7AyQmiveQGm4NUbg65hqDzbde6q9E1b2mNtuakz4pump": "MOONBALL", - "4zLQNkPEAn4oSVtFCeaJ7cZkZAuD9ysHbMRVm4typump": "MOOBA", - "G7vQWurMkMMm2dU3iZpXYFTHT9Biio4F4gZCrwFpKNwG": "BIRB", - "FNP2Dw2GU5wPBZgVCcerbEemV9dVfvQrThEor1jmoon": "MOONCAT", - "D2QvT2fgdvaLxDLiTFjHeRqeZFXU8UqFdJr7xcgHmoon": "MOONCOIN", - "8vVHBmASTSFAYEFNQg3qxQQJQ5vBoAVRWYsdgEahpump": "MOON", - "86N5k7YfzPvdgRZ1ASE3fhKniAwc6GwaZwXes7ssmoon": "MOONDOGE", - "yM3AbxK4B4iTLddjGGW7JeQaVLzMiF5y81vK83fowTn": "MOONKIZE", - "AMdnw9H5DFtQwZowVFr4kUgSXJzLokKSinvgGiUoLSps": "MOLA", - "7ybKUMNTuK9it2mFd7y5P4RMk7dUU3EqSffRm24Vpump": "MM", - "99YFbXS6iA7s1qkRDTMBXEywtLNmrRtHtGKvNxpKWcdT": "MOON", - "8x41eXaZhA86vHnyovggfvTGZYA9twmXG1BMLhyVpump": "MOONMOON", - "Ai3eKAWjzKMV8wRwd41nVP83yqfbAVJykhvJVPxspump": "MOONPIG", - "8PVFdKx7fX1FmmoASfTu5CUPEWuD1WY73Z24UTgCpump": "MPUP", - "moon3CP11XLvrAxUPBnPtueDEJvmjqAyZwPuq7wBC1y": "MROCKS", - "5HsZR8eG7QpQcN8Mnp8oFdENRkJMP9ZkcKhPSCKTJSWh": "MRC", - "FHHeb1ji8Qm5Nq68S8zPV9HCPbE7YBdBmV7fgqYRpump": "MOON", - "BqhNdGtS1Nqtzi2MvZ7G8NN1vRuHZ12UpHGJKe71e1JT": "TRUMP", - "BD78uBLz87HSw77mSt3zCTkxpFgBE2HJuaM8c4Es1ray": "MOONUT", - "EBjCSRYrGJzQtqgyddaH95LWU3sw8YMjK8CgwNoWpump": "MOONVEMBER", - "moon18sDJGhymHnyEMHWPdqjmSykNhFP77LxPZoueTt": "$MVB", - "moonThZEkkTVoNB7v6YVCQiT56JYDZ1oN185ba3WizL": "MF", - "EqKmC2f6fQsE5WVzXKkExrnfBabohHvSrkf5xxjwpump": "MOONWHALE", - "7Em2S9HUxrMm7mDA84j3Kg8N86s35AzhrotHQcUwpump": "MOONY", - "6P4yBuWs6XnFf9hwyDsHVoqP4PnKtzKkcLhm4YFTyvWz": "MOOSE", - "HN2n82rahixyTQ6LarLjSVRR7YX7GZTmmRL39Vvdpump": "ENERGY", - "8ZHE4ow1a2jjxuoMfyExuNamQNALv5ekZhsBn5nMDf5e": "MORI", - "ATo5zfoTpUSa2PqNCn54uGD5UDCBtc5QT2Svqm283XcH": "MORK", - "34HDZNbUkTyTrgYKy2ox43yp2f8PJ5hoM7xsrfNApump": "ROUTINE", - "ACu1RKQWdXAyoXvKzfSDsFKCqGfyBxngXq8YnmBPGomj": "MORPH", - "4FysfnLNVkjKTkEZbPkbvrfBdHQhXfMbCLDsFRK5pump": "MORP", - "HqqnXZ8S76rY3GnXgHR9LpbLEKNfxCZASWydNHydpump": "MORRIS", - "FfoqzbWHM2U3cuTWtJvPBfVSyYHFsaUVyLHriVBHwYN": "MORTGAGE", - "FskzSqy7Pi1f3nWorr4WhhQboxzyv8fv6Q2e8xyDpump": "MORUD", - "7U7FZaLfJTt2pN9Canp1Njt3vbz6eiqHbxn2bpmEwtYB": "MOSHI", - "9bNUjxEvygayUE2ZRN5zh9Hhjh6cN6GPK4zoHzXXpump": "MOSS", - "7cyAGa4XsNDnSgnFuwmVFgp9JGqZoZYkiVyFPSke9CuP": "MEME", - "EfRr6xyFYAwVLSV2sYt68gXwPFfUYayGpEyRoSLGRS5L": "MØTH", - "C2MvcWD86peRanQAnJfiDPurs7e7CkMsnP1bHBNJvBpA": "MOTH", - "3S8qX1MsMqRbiwKg2cQyx7nis1oHMgaCuc9c4VfvVdPN": "MOTHER", - "FFVcrc7wxCQ2HsxY6jhKNPwWUeKJsL472K28Vw16pump": "MOTION", - "5qvPyRS7j1cBYgFuK1umYjqGCdP9qLwfYjHBCHwLbonk": "MOTION", - "7PagSBusvxQ252fuBFnhipMZwd4T4jGikLeuwrDibonk": "MOTION", - "Uiupt55owCqBJdunYQxyYf48KRMU4F53xQAyf31dpzx": "MOTION", - "uPXPhEXb8cP1jkd1xrrbxJSMt47TSEEKRvu7jS4pump": "MOVEMENT", - "BVSv98vyMEkjwCd4ZSBcPUB8MjDPqQfVHTrbCGtYpump": "MOVICH", - "XwFm5GiKPVTvPiEbQpdc6vJbFEpsUXRMf6TcSxnondo": "MPON", - "pv5TUu7tVbV9NgqBrfbEfQ2bTCULuNXdbP4kgHrpump": "MPOOL", - "39Mzpdw7NDGiXmZZGWiCdR6Nzoc7muWuYkPsVDV4pump": "MEME", - "GzxpqHdQeseerHTM2Gikq5F4o8Bb1oTENRTJa8E2pump": "GIVE", - "3MoLRueScRN3kHKbqwZnyjtPGoG5uVgKhAwMEVNjpump": "PUMPT", - "mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So": "MSOL", - "8x9c5qa4nvakKo5wHPbPa5xvTVMKmS26w4DRpCQLCLk3": "PAINT", - "3T5gKSRQy6ZbUJwGEhDL6yg73nTaLVax6TagkcTDpump": "MUBARAK", - "Fnm6Jf4BtRHSjMcUt7jYk5cJksQhzuwhtgg94kG5XB59": "DNA", - "J6F9W8CLa1afkCdbxDSAyMeXo9Vsfvprv9QzHYMS5HgP": "MULN", - "6gvWSka7SnJDn4mqV7Ydn4q7AyVNSo2aX4TNGjBg7Cct": "MUMMAT", - "5LafQUrVco6o7KMz42eqVEJ9LW31StPyGjeeu5sKoMtA": "MUMU", - "8ovs151K45m77kWeDUYaRuNyKebpiNfwr7SrgFShpump": "MUNTZE", - "FtPqJ2YTKmPyBxp3Npg18RE9Z77SQu9jrkRVYLzpump": "MURAD", - "2YDMeite4gKusVQGAXvQPnbEXwkLKm2pUz8CTqHBpump": "MOM", - "CQ4PZEmJkR8Zchhfp21fkoCAJuZiv2dhA4QfKqupump": "MUSH", - "5Jr9hGmJgxBRjjF8XGcGgQzXUdsbpZNNMpigEv8Wpump": "MUSHU", - "9So52ugZh2BLBT3f7p61947q91uQh2DyvbfyMDeRpump": "MUSKIT", - "22Xeo6diWfJrScaoVFzgkwzrCByPukK45fdwkJyrpump": "MUSTARD", - "AMp6nNmvohjGYEnfDZ635nykM1msQTRAhmquUnHBpump": "MUTE", - "C1kzNkFfgdtP8VF1pFYA4S32RLPqk5KPaurCaQJwxfWb": "MUZKI", - "ASNoTS4cYopuUbmDMWM4AU9xdCQnb5zPe3gBWfTUsLTE": "DEV", - "A7qn22uQNpUHWmSk5GY5N4vmVJ4uPUzGySegdkm1pump": "LINK", - "8Up6R3g6qdpsApu8DUCtajSAWANxSty2eQmSjCSGSHH6": "MLC", - "5KCGJJpRMtbydUDTAoc5MoRPnqmJNNDaFwvBDdSG6daC": "HIGHER", - "H1aN3vcvB68eaFPbMkoAss3vnfi4AhP5C2dpnrZzdBc7": "MYRA", - "HhJpBhRRn4g56VsyLuT8DL5Bv31HkXqsrahTTUCZeZg4": "$MYRO", - "mysticrSzUfD2pz4RayXF5oEHGfNNAFsSY3z5hTQZSN": "MYSTIC", - "dgU3DSSTFyHx4j5MKx94V3BB6jMse7EQpGDi6zUuULt": "MYST", - "5UP2iL9DefXC3yovX9b4XG2EiCnyxuVo3S2F6ik5pump": "MYTH", - "HB99pDdRG7mBpzWXkmvDJF3XCaDQ5HBEow5zAj6RadF6": "MTK", - "HBRPNwdcT67cadiBS8VNP4mRt8ornngLF34TZXthrust": "N3ON", - "NAE5Rik3FrGoXoqoqW8boyCLrgEDnEKHNfPAVNNdQJr": "NAEST", - "mkvXiNBpa8uiSApe5BrhWVJaT87pJFTZxRy7zFapump": "NAILONG", - "Fxm6tJ2khHbLVcSgnYUqg9FiBduVRLEvdQR67dvQ3eGp": "NAI", - "CKVZPWFPaJArEaPnk16CXpFtFXjuCxCh95vBcS3Ppump": "CRABFURIE", - "H9muD33usLGYv1tHvxCVpFwwVSn27x67tBQYH1ANbonk": "$JIM", - "BWJ7zJauzatao4FsBnGdVsqdBi3k5NbgSY62noZApump": "NANA", - "HxRELUQfvvjToVbacjr9YECdfQMUqGgPYB68jVDYxkbr": "NANA", - "EVqkPanBKrx6q2cuej5CwjdEoK8CM2UPdYi9nmSPbonk": "NANI", - "BVFGj2ww3sz1Mc2MgFQqkDfjmLVmTQD7MU6NVvS6pump": "NANI", - "B3eYHYLwitHsPd5d5URKUYxwjwsugdmp34EgPEHrpump": "NANOBANANA", - "sctmTAsDn4tLUcemqoqYijfuRkiEfAMPi84PNq2EueR": "NXSOL", - "FYFq8aYYpRgwH6x9Ez4M1xEnv6aWpMrtdhYRUhbJyo43": "NDAO", - "Xs8S1uUs1zvS2p7iwtsG3b6fkhpvmwz4GYU3gWAmWHZ": "QQQX", - "yCYSJATPJiFgtE1Up7LFGQEV8XLZ5Ma7Y53JnXKpump": "NDAS", - "FRySi8LPkuByB7VPSCCggxpewFUeeJiwEGRKKuhwpKcX": "NATIX", - "7wM4MnbsPsG95A3WhZgbrPWvMtydKmJjqKr2ZVJVpump": "NAVAL", - "GYTd9XbZTfwicCV28LGkwiDF4DgpXTTAi2UeCajfpump": "NAV", - "5qmykKi3zkM1sGvcMNgj4bqy68qvpLqR9fQWWrtfpump": "NAZAREAI", - "3bV6a2fBT9EzWBbXiphnLMmmAEQ1faYwppCY2Fnhpump": "TROLLBOY", - "6CcbeMGmV11Rh6wFC2LE5ALEjBUUBCiHfQY2zw7CFQRK": "NBMLUM", - "DiRshqNDE68bWbGdLHm1GwQ76MvWQG3af6w1NdQondo": "NBISON", - "Hog8azPhaoZeXhcBF8DZ3TZAq8AXcX885ZBX3zhP76fq": "NEEDL", - "Ce2gx9KGXJ6C9Mp5b5x1sn9Mg87JwEbrQby4Zqo3pump": "NEET", - "CTJf74cTo3cw8acFP1YXF3QpsQUUBGBjh2k2e8xsZ6UL": "NEIRO", - "CTg3ZgYx79zrE1MteDVkmkcGniiFrK1hJ6yiabropump": "NEIRO", - "BJ2ENyjuBGUQJr6CCDBcGAQNQDbayoNbDziDGrWSQQ4": "NEIROH", - "GLEmV9TBLJBK5orHQLgU7x4a2nEzopy25jR7iHk5pump": "NEKOMATA", - "5hUL8iHMXcUj9AS7yBErJmmTXyRvbdwwUqbtB2udjups": "NEMA", - "GiN6u4pYqQB1tUNCTxN8n8aM1N2hDmtqaKWCSiAv1uLW": "NEMO", - "NeonTjSjsuo3rexg9o6vHuMXw62f9V7zvmu8M8Zut44": "NEON", - "53kC8TJ5gfBPBPN4afNA1k433vL7ZhNHaw9NGh3HBo9N": "NEKO", - "FJz7ptUR1FwxSHbb8Sfd5Cn6Zt4TqucJZKLqU4n66gdq": "MAY", - "bj7xHohoXetk461iGPSAoDS62yiJsmxgV82RkA9moon": "NERDY", - "5HTp1ebDeBcuRaP4J6cG3r4AffbP4dtcrsS7YYT7pump": "NEROBOSS", - "AFoiQ3Go8PTe7sGy36GsQbMVMcFqJMSCFuiqoDuzpump": "NESHUDO", - "7nFYpHyhDgzg3ioU2JFko5kaVwm6bjkL4MtbUiVAiYbJ": "NET", - "YeK2TdPtGLAme3Phg4pb1GBN2YxKgX5UNVyD4asondo": "NTESON", - "g4KnPrxPLeeKkwvDmZFMtYQPM64eHeShbD55vK6ondo": "NFLXON", - "XsEH7wWfJJu2ZT3UCFeVfALnVA6CP5ur7Ee11KmzVpL": "NFLXX", - "DH7HuYR54FNp49isrus6AdqREuQfWYV6qaVPg8xVnZQ9": "NED", - "B45FhPi6d7UR66TGLpQ3EPnWhGfzdGVkCKNp9neR5Cxt": "CITIES", - "Dc2mot4v1rZn6r9taoc2Mmqi3LM1h8eUbHtkQ1Xupump": "NETWORK", - "82Rc22mnyHrmBGwj15rhYhFzVrU3bgFkjNtV3iHjpump": "NCAT", - "BNC5eVJZixJFykygncDuUK5TpRBsWZuT6eSA99YNh2cZ": "NEURAL", - "FrQNn7xSTQWdv3SC8stZfT76QeWgvFWVQtCJhdGVjHJK": "NEI", - "GFSeaid3PrKr3agHd9dQt3qFEG4Rxosr7U8aLSsEpump": "NMF", - "CyQr3CGBDd4gTncX4qSRMB4wQqQyRLY5iDgKHjhXpump": "VISION", - "3N2ETvNpPNAxhcaXgkhKoY1yDnQfs41Wnxsx5qNJpump": "NEUR", - "Guko5GPz4g6E2cjDHukGtdnDNJeQQvpEW9SCgRZcpump": "NEURVONA", - "Gka1TQEevBbVw4W9K15ER96ZzKByMTb6TBMQzWFEpump": "MINER", - "6T7xYGQxtSRjKFpJHvX1ondUnULLLyx8iKsGmM7Xbonk": "NPAY", - "CxrhHSqyW8YTDWc4csJMMgo7uBUJSXzNzrWhtw9ULdru": "NEVER", - "EiKZAWphC65hFKz9kygWgKGcRZUGgdMmH2zSPtbGpump": "REMUS", - "AXTCLqqupJN3YWikgDtAZxRKWizsbePDyV9XtkDtpump": "KIRBY", - "U237C8hKyZYL42TEtTv6JGtdwDr3pZaQeeWMCVVpump": "HAGGIS", - "97WQm8aUu2gprFzEYfGmdJ7wcF4NSDPgvn3hvbDHpump": "LAKKHI", - "HHcsLmPyZoVpHP6pVNyBf2vzxpeMkxgaZw9NJZyWpump": "GNOCCHI", - "7DRmRvxK9G6a5uMyFdfa7UaQC6dRAZz1dgioy9Htpump": "NEWERA", - "AVKLphLUpoNCwiyUHt8PuK4vz6JFkhRFni7HwZKrpump": "NEW", - "Dig28Tf1ufhCBAsjTmFkXCgcNgMqDMYj5A2rDQmondo": "NEMON", - "FY7EieWUE7ifbQoABC73w6bxjuRtc53PdJAnojQ3pump": "LORIA", - "2Ar2n5DJSiQuMhY4XeSzg7C5AGMezZPoLUt98VmmjsAR": "STATE", - "38PgzpJYu2HkiYvV8qePFakB8tuobPdGm2FFEn7Dpump": "GORK", - "5AmQVkVnELiBV2cLrSWfBePmGj85vLXCG5vkMeETpump": "NEXA", - "9JErKCoJX7nmMsYvicoZfEdTdW1qiF9oeocHWyKLmoon": "NEXGENT", - "t7eN6cGwRMFaZvsNW2SmVwkedmHtDdrxA4ycNE5ondo": "NEEON", - "2Zaq3WyDJj2ZkMcuqUiTBfAXCjqZtqihxcwUdwv9dyHA": "PEPE", - "8xTDsy1UFBYK1J37igAdwRDRqvgCEUJYe1QggFNApump": "NCRS", - "88dnPHaZDxwCm9xiDgB15uPwGdgE2y7ALcqsgRTopump": "NXR", - "4SLbbckLuMiUPFBT6FrqKtHd1j97ePmG3HDJUd24wBLV": "NEXUS", - "NFGgKCroEe2EucBJmFfCUNmU41MHv9Sc8qUhbceNTvy": "NFGX", - "NGMiXUJbCVaa3cYggper7fZudvG6MFk6DwsnrULy65Q": "NGMI", - "CJBSwuppGWjBQfCueziH1czsyQp7DuW2wwYedC31bonk": "NIA", - "3gC8oidaJ61fkB2QCYvm9xKMZG8szBgBMbuLAGgNeGJD": "NIBBLES", - "7fj85y28pKMndm4So66Szkb5GMGfLHFEkwsZdDx2pump": "NICE", - "BEamfAJycLnX3ij9mR22T71XWdruM1uQik8TyL3Mpump": "BABY", - "5zy77ie2LVoLaMDy2h4SAvPCo3uc8Zno85YGZDZjpump": "NICK", - "BfFU13dyBbYT5E4dZdeFHe5YeH6p9DdTh8fheThspump": "DOG", - "8Jx8AAHj86wbQgUTjGuj6GTTL5Ps3cqxKRTvpaJApump": "PENGUIN", - "3boRKAxWR6weV6kufr9ykdLcm9cL5q2p469tCqeCAnHy": "NIGI", - "9xK2Cj2tKq1jzyjr6BtdqHxe2niTtshxF4Y7PTHMpump": "PENGUIN", - "g646pcdG2Rt5DH9WZzL7VVnVDWCCMTTrnktwE74ondo": "NKEON", - "d8ed1gwSmD45qwEK5wZGMb1Kh1MQ9PXipKsHevtpump": "NIKY", - "FgX1WD9WzMU3yLwXaFSarPfkgzjLb2DZCqmkx9ExpuvJ": "NINJA", - "G7iK3prSzAA4vzcJWvsLUEsdCqzR7PnMzJV61vSdFSNW": "NST", - "DFrJxDoLMYt6bNYeNe8Wrjzj2UPUSLZLEMMYBLuTKcTk": "$NINJA", - "yQ37dFiGAbzrb2FRAEhGNzRy5zFfoYGWYhAepFEondo": "NIOON", - "5DkzT65YJvCsZcot9L6qwkJnsBCPmKHjJz3QU7t7QeRW": "ANA", - "9DZvu8yahirm6NagRa7Cm484VbUwZYKRm3xvpMrbpump": "PUFF", - "ESVRQ6phc55VCw7sWB6JgW3PeTB6N68kvwjfsMPcpump": "OMOCHI", - "AYABiqKuTh9Va5Aqc6AujFevHwDGmECGQiFmKW5g3K4Z": "LONG", - "C29ebrgYjYoJPMGPnPSGY1q3mMGk4iDSqnQeQQA7moon": "NOBODY", - "B61SyRxF2b8JwSLZHgEUF6rtn6NUikkrK1EMEgP6nhXW": "NOC", - "2kQXaTMyf6tojju9bx5Hq24ahdAHBu1E9dkfkxSfpump": "GHOST", - "B89Hd5Juz7JP2dxCZXFJWk4tMTcbw7feDhuWGb3kq5qE": "NC", - "EumKiGHzr894F731MV3heFQygNQ5qrYVBtvfQ483node": "NSAI", - "8zbUbQSt1QvGAPr7r2SQ2mH3SzYDkbGddQrKPiDbpump": "NODIE", - "7rnxiXfV4kajobuZ2qBHjVPBhhcT82TPdzF3cPfHpump": "NOFACE", - "FTggXu7nYowpXjScSw7BZjtZDXywLNjK88CGhydDGgMS": "REX", - "9sgZAUEyUsifAjrzsD74cCeV3dMzdeEswAJAHxoWpump": "NOMDOG", - "6ZrYhkwvoYE4QqzpdzJ7htEHwT2u2546EkTNJ7qepump": "NOMNOM", - "NomuBwKJEvJ8d4dsaq7NZoHaXWXzKcfke1c7Y8ruFYL": "NOMU", - "8ATm1hGfhkMhm38DREUQPiWU8mhFzAupqkNv7nhmJVNF": "NON", - "CuEPELFSwRcs4nQ4Dn6a7RVz9ZRVoa1rBapcqG6ppump": "N0NOS", - "BeGY8KqKxboEwRbJd1q9H2K829jS4Rc5dEyNMYXCbV5p": "NPC", - "6nGnXdsabPPJ88CsrigJUA7QY6Ag4PbRZkRJqEKypump": "NOOB", - "7cEgQdp8JTXvBrpjSzji7bLEeCHWENRwX62B2Ep97k5H": "NOODLE", - "4E3CrfKrPWmnE2kFrdeRKyiovpUWpUXHtqXpXzZZsxZ2": "NOOOO", - "5Vt84ukEKHuxYA7wjH3PDwCzTGumPS7MDNGX4cFLpump": "NOORUNG", - "3Yie9s6iuEmk8aiXYC19xaq2iccTuFzyfKBWv6gFpump": "NORA", - "9M26M7CxkJdaewdiH8v5kSiiXKbWBBirTE1QmXHmDvVg": "NORM", - "FrSFwE2BxWADEyUWFXDMAeomzuB4r83ZvzdG9sevpump": "NORMIE", - "Dm6FpQ76SsbVmAZ4NvD2mjZP7cxbw1CASr4WwCiondo": "NOCON", - "BwcamVymEARNdx2chNraY9CpJgKHUQk9Uj7m51tLpump": "NORUGGIES", - "nosXBVoaCTtYdLvKY6Csb4AC8JCdQKKAaWYtx2ZMoo7": "NOS", - "4Bf3MmVBm94pMcXmnUfWrG95AApZHuEbGoztdGAxpump": "NOSE", - "GZcVdxXSenrKgnkAKCj9Yp7ConTB4poGfBohFVpupump": "NOSEY", - "F2HE9krU9HkpnYvv9LKom3hB3Nvm8uZaThrmiE22Zdpy": "NSTR", - "E4x2n6RLMPTjw3vqkCxZkEgiQXhDEHHByvwUWttL1uuD": "NOSTRA", - "B2GzD514ByBioqDY2XoZZW23mVXA5686wPotvWi41GF9": "NASTY", - "Et1nwX1U2PrS1A4iRvFcd6LGzYWh1C33iHvKWBCVpump": "NOTBTC", - "6gh8b2mqUrVb6Ew8Mwt89tNyN9keCDNstzT5cdp5pump": "VOID", - "FD6vGnA4iewV1B6TVo7e6MApusSreE2sBY3LWj5Bbonk": "NOTHING", - "iNKezFtQnxqjX8JMrez3pT6VaV8VwhA1SZXTbbzpump": "NEES", - "7nePAcJhg88s1KFJptgaBvzYgHaUDzddgU65Etvtpump": "NIRE", - "NotMMjBCRGKg1eTZMhiYFbfGJNLj2ZHt6k4fZqchA35": "NOTMEME", - "NoVpnNV3Mj6ozo6SF2E2DAGkQZ1DmBcsKuSL1xz5a69": "NOV", - "AfLh3wndb2yHQWJZFSjyncvRDDyU8DQPcGbY1eiABdev": "NOVA", - "GtMtXoJiqSf8Gfp83cuunnDTiJTeTmv7cniVtJ6UAMWH": "NVX", - "BDrL8huis6S5tpmozaAaT5zhE5A7ZBAB2jMMvpKEeF8A": "NOVA", - "STKmfGGegeoYqrHrJ3nvTafSvRC6GJJBbLwN1cqpump": "NOVA", - "3SkFJRqMPTKZLqKK1MmY2mvAm711FGAtJ9ZbL6r1coin": "NVAI", - "AUDDttiEpCydTm7joUMbYddm72jAWXZnCpPZtDoxqBSw": "AUDD", - "GeV7S8vjP8qdYZpdGv2Xi6e7MUMCk8NAAp2z7g5ondo": "NVOON", - "XsfAzPzYrYjd4Dpa9BU3cusBsvWfVB9gBcyGC87S57n": "NVOX", - "Eq39Q9bwTx1YHY5pkHLVeahVA6JYZmmMJ71rPjQek3Hb": "NPC", - "GdKWebBBvM78HNYGxoZLgsXHqZKAELEZk3ka9i32pump": "NPC", - "5ToDNkiBAK6k697RRyngTburU7yZNFZFx7jzsD1Uc7pK": "NPCS", - "xNPCScK5Hs9ywamnLNsDcxGG9bP3CKaGjcP8xE5KGNy": "XNPCS", - "GXUisEapYfHJe8oFoWSPPyiLSLYT6Em9iEeXTFxUHyUA": "NSDQ", - "vQQ3Z2j8Rz4bupwENK9rhGkpgy3Bj6LdUTPz9J2pump": "NUF", - "HkVRqeohQAGa7oZMrGPs6bFUf6aAmFdQfCAmtN3PXt9s": "NUFR", - "2eXamy7t3kvKhfV6aJ6Uwe3eh8cuREFcTKs1mFKZpump": "NUIT", - "HthQKKW6M9Q8pb27r9xoqjfviiJ3kzdojP8TKqjJpump": "NULLA", - "D2jtkGU93qikXr2dpftsStLs3EXEZy62ww4VQpM7pump": "NULL", - "AD6StJzK8sFJWUetQdsnfHWrmmEs1dtSYuuZLrYkQUy8": "NULL", - "B7tP6jNAcSmnvcuKsTFdvTAJHMkEQaXse8TMxoq2pump": "NULL", - "48eKhwwadm7LJ57msuDYdq36CXx23Ratdbu74Pa1NULL": "NULL", - "9JK2U7aEkp3tWaFNuaJowWRgNys5DVaKGxWk73VT5ray": "NUMMUS", - "HeJUFDxfJSzYFUuHLxkMqCgytU31G6mjP4wKviwqpump": "GNON", - "q29umWshmh2fmm1CdRb4cBKhqtW9xX25ezNQi7Bpump": "NUNU", - "7HHCwGd7UAPF3gtHExBREYgY37cbyYcCaeyAHKUc7mUn": "NCASH", - "BoyGMfttsVUYK3n6WrSeWEeqT94ExcTmgJsYEkJVpump": "NUT", - "3KkQ3AaMs2nZoKvYzGnh5bwRocAGFFFzDWUR4hLT7JpF": "NUT", - "gEGtLTPNQ7jcg25zTetkbmF7teoDLcrfTnQfmn2ondo": "NVDAON", - "ALTP6gug9wv5mFtx2tSU1YYZ1NrEc2chDdMPoJA8f8pu": "NVDAR", - "Xsc9qvGR1efVDFGLrVsmkzv3qi45LTBjeUKSPmx9qEh": "NVDAX", - "NX8DuAWprqWAYDvpkkuhKnPfGRXQQhgiw85pCkgvFYk": "NX8", - "BAsnXPVYuvZDfEFR7tmu9sG9gPyHy58Jpjs2AuUw1FLx": "NX", - "NYANpAp9Cr7YarBNrby7Xx4xU6No6JKTBuohNA3yscP": "NYAN", - "CMx7yon2cLzHcXqgHsKJhuU3MmME6noWLQk2rAycBAGS": "NYAN", - "Ho5fkxk9uqUuVN5kt7TUFacduWCpKPAuYkM9HD3KAXA2": "NYC", - "7NgzCgQ8vrosdtxUonHvWETQ41mL9XQ5Z2JqJjhQpump": "NYLA", - "EfqR6H3knDCgYq7QLtwjRAC5wPzustQvkzNTAMzUvirt": "NYX", - "7W4geAJyy7hxuPXESMMBaW8Zi4MbhVN9uvt6BJ2SEPAV": "NYX", - "HrLx8MLKegpbmbmWePZiuvf3AbJNx1CJyjBwUHwicEgW": "NYXC", - "H3cHwjGfcAgm17Q4w68od2za4epqpH7gv9rkbAA4pump": "NZOS", - "G5oL8AR1pd4t4xxGheZjS1DBxTBLJYsT9ukjcrKc5vJH": "OASIS", - "CzkM8bzWFdXsjtZQnz2piTxJKPtJ5mfTL8S6sNZg7n7S": "OBEMA", - "9NyLK6MHjbaKd1bY3EnFPJQDmkDLvC2WajUCErp9pump": "OBEX", - "5WGkdemJNEoAVKveDQdzQmDqTyxCraXGw9ACyrwCEUMS": "OPK", - "8SQQ1urC3Dynq9C2ieM6AozWgi4GCrLE6fnRoiWdpump": "OBVIOUS", - "ocadaAXpD1haGy2JnjHbgUhczqtiMdYHzvnBihjJHg9": "OCADA", - "1GNFMryQ6c9ZpMhgNimmsbtgYM21qnBJgRAFoNiondo": "OXYON", - "octo82drBEdm8CSDaEKBymVn86TBtgmPnDdmE64PTqJ": "OTK", - "4CoTCzobYt38zVbSieZxcmz2CCs8kmZJ6wnbj8HWocto": "OCTO", - "DEhDXhtUFuz6Uodhde3rznGbVbdiECahp1kTHnFpsna3": "ODIE", - "odinyvt9FgWWxw9BwZFjf7Agcch6Pk9mKubszw4izAG": "ODIN", - "CBdCxKo9QavR9hfShgpEBG3zekorAeD7W1jfq2o3pump": "LUCE", - "caer1UmwhRsfFk5huGYkXcFmiL2tj4cyT7YjVxUpump": "BTRUMP", - "7VRvzb8XExciDb98kPcFC8v3P3ALbW9bhTUQQhBzCBPn": "OCN", - "Dz7DKLAbAbzBXMDcLUFCVzd4PKKDtZCtvjKJ7Wykpump": "DOGEWLFI", - "JDzPbXboQYWVmdxXS3LbvjM52RtsV1QaSv2AzoCiai2o": "FO", - "5T17aqgJ8cM39SNuVBu2LK2cq5MWUpZxcQnnuwNjpump": "LIZARD", - "6ymxqmDH8HPgheJMhLYdrM9exvwZz8dsR5DJBvnXpump": "PP+", - "ch7rTovcUK7C1zNFM7F93kXU2CByYUsJT4pwYx7pump": "PP", - "CqGMgsUDbj1XnKe45FbzZLd64MivoKPSgnZQJb3emoon": "FIT", - "N1FEMCzP8gEPQ1HeL32v4wEUuKTz3X7KMGRM1jgpkin": "EXOTIC", - "6p6xgHyF7AeE6TZkSmFsko444wqoP15icUSqi2jfGiPN": "TRUMP", - "GAYCVRGZH2tHms1c5sCprE2JEbuz8tJ9ZxCNUX1cKwWR": "USA", - "5m9AhhwMnosop8CAWdSJDNfLLkg3EFkX5sJ3zAuMpump": "WILDNOUT", - "G1VhWSAhYXRRzokGqxs4aQ8u5DSuP69ki2bN7hnapjzZ": "ZUNO", - "jE7q5qieKaUXmyhuWTXmGVtpeBoKtgbMbtks7LKogme": "OGME", - "2nhStpBLy7umUR9NfhvurKoeTf4BJV5ui6Z5sHA3jaZf": "ROAR", - "Fa7mkzDfzkgU2iQrwzpAvrpQoLFnMM85qnZCdaK4agQR": "OMG", - "6FgPuEv5gNiHCTJqQeWiY6vu79fuSSqYJkHrpdompump": "OIL", - "kLf5mp1wpHfJPhHxgM1DEDwJherwWrz91txmQagpump": "OILCOIN", - "PqXub2t6A2vvUb3Mevk4uhj339rMhhmxq2HQzYNmoon": "OILX", - "GJqCjtgEwqdFWVRsDs8JXKFoTeRVZeHs1RL4ccvrpump": "OILINU", - "o5XguNKkz71K37BJsAhyMJnatwwbJ9MdixSmDW6pump": "OIL", - "8RX8eViRE9hDZucrhQ9fUfiGejvV29nkxu38ugKzY69d": "OINK", - "HRsTTwQn6FuC347F1k8V9NpfQSY9JfDEiwh8Cxuipump": "OINK", - "AKzAhPPLMH5NG35kGbgkwtrTLeGyVrfCtApjnvqAATcm": "OI", - "H7N54K1TjCgRtRLanZaPaDczdKFBTAiA4grvcVuArQzr": "OKANE", - "8biqsLDiRPaPM11S42zKtNaC3WWKF64PNoPG9htzy99n": "OK", - "HvPrYvgJiBtwUC7H4bi8L5bqnirFPWc2J9NdVy6UFiZm": "OK", - "m6oDLvJT7rY7M1TxuLWP3pWmAPg2cCWDQR1NKiEondo": "OKLOON", - "XsJb1p4Ks6VFggq68JyMz7S3kdRfkqh4wA6EMyxi4DD": "OKLOX", - "CtzPWv73Sn1dMGVU3ZtLv9yWSyUAanBni19YWDaznnkn": "XBTC", - "H5tnN12NCWqa6zhpmY2ykvmreEUiRaquqtQiPayWpump": "PHOENIX", - "2Xva1NeLRuBFdK41gEuXqgeWtnKKDve9PKeCnMEpNG6K": "OHM", - "3DkVGaNSMTcxFkgGDAm299FaVxgnCw2411vxZTmRpump": "OMEGA", - "FTBq3w9gCv27E451DV6w8AbUjBQbyVxxj1ZaYEYepump": "OMEGAX", - "4Aar9R14YMbEie6yh8WcH1gWXrBtfucoFjw6SpjXpump": "OMEGAX", - "A8vJzs6ygbZQyYLf9FKund8j3pDfUzxBeje8rJiypump": "OMNI", - "A7bdiYdS5GjqGFtxf17ppRHtDKPkkRqbKtR27dxvQXaS": "ZEC", - "7mmXL6Et4SbpDs2iXoZQ3oPEeXeAiyETxh1QjDNi5qnV": "OMNI", - "CgZTsf3rNnXsy3YkXmRr988p1Lrv9FpqBpLPWrAbmoon": "OMNI", - "omfgRBnxHsNJh6YeGbGAmWenNkenzsXyBXm3WDhmeta": "OMFG", - "Fzpo8vGJRRB9d8h9hv5FdNuvskTSXc55GGFvNpZupump": "ONBOARDING", - "7qy1j4Mechfyr6uAST3djH4vk4kiEYC2cjEytXdondo": "ONDSON", - "CJRXkuaDcnXpPB7yEYw5uRp4F9j57DdzmmJyp37upump": "ONDA", - "A1KLoBrKBde8Ty9qtNQUtq3C2ortoC3u7twggz7sEto6": "USDY", - "2L7zBZsKLJFMqQH2w7rUwiM6eKKg52w3iyeu37fMpump": "ONE", - "4PbBkuZvub6Ugj8KwCCRsqk1Ya2defHXn7RSLbbEpump": "ONECOIN", - "CEm2Fg6k1o9GKFnfzXqZyTE1UcfUw6dGt3Dsn37UHBwJ": "ONE", - "8tKWk9c3o8sbvcqTBePv2B99ZTthMdXAhSVRQ1zApump": "WORTH", - "of28WzrGVqVKCC35zjkQBrndCCDzErtzg7cj2mypump": "PUNCH", - "7p2Rs9KkiWNKZkWNDFUCxG7EUBxbCByQC95fCNQ8pump": "GAL", - "CwbpyHPZJ133hgWQvd1hZA4ZxjQu3mL7WRxEhmqQYkCB": "ONEZEROS", - "CxN2KctowQ216p9wWRgiQeMe9ExRSrL7UeR2UYRzpump": "ONGO", - "8KDsEUE3NdafcCVPqKrMrPfqh3oT4FzcxsYjwbMqpump": "ONIGCHI", - "3bRTivrVsitbmCTGtqwp7hxXPsybkjn4XLNtPsHqa3zR": "LIKE", - "BqVHWpwUDgMik5gbTciFfozadpE2oZth5bxCDrgbDt52": "OPOS", - "onoyC1ZjHNtT2tShqvVSg5WEcQDbu5zht6sdU9Nwjrc": "ONO", - "13qtwy5fZi9Przz14pzo9xqFSr8QHmLyUpUCvP1xondo": "ONON", - "3AZAjwZZCe8Jf4yccheZ5Z1c2SPEPyoYJHFT3Le2pump": "ONSING", - "29m5CkFSZSxKnCn9v7nqANdMTZXoNKydTNzsEcGDpump": "ONTOS", - "5Y8NV33Vv7WbnLfq3zBcKSdYPrk7g2KoiQoe7M2tcxp5": "ONYC", - "4ZfitzW524FUvWrGuNTg3x7d7cfCuNZzUaNEiB2BT5Lj": "OXA", - "EikyJKSVWPK28rX5FG8KyJcSzv3D2b2Qg7VodzqQoobe": "OOBE", - "oobQ3oX6ubRYMNMahG7VSCe8Z73uaQbAWFn6f22XTgo": "OOB", - "48UE1jHd1YRR2EpTnRXi6dA15eboq92jm1YusW2epump": "OOF", - "4QEFktCy1LzPyQyitkTSVEkd9m2AYr43ZJ598FDwbonk": "OONI", - "2VRC6F23CjLv3nBHnNw41ua21jACWKcFmbWH4KohNKWU": "OOPS", - "Cck21qHq33rezTeuQbPSvULXF2vLUTMErJwbPdUGpump": "OORA", - "4HuSTfcJruukNbhrF9sc5iKrMeGxNBs6XpQ1L2hUpump": "OPAIUM", - "2PzS5SYYWjUFvzXNFaMmRkpjkxGX6R5v8DnKYtdcpump": "OPAL", - "ANV67ojLzxLeout1zi1q5yZAKdUTmhMjH5EaZn3zpump": "OPAN", - "3Ydb2n8vAFdBJYpdiZEoDxRXLmiGDY1fuizVmMPMpump": "OPAN", - "PreweJYECqtQwBtpxHL171nL2K6umo692gTm7Q3rpgF": "OPENAI", - "76uBMvo1WL644VaUMhnuzSJSLJtQbNvr8ThubQi7E95n": "OG30", - "ou1uE526v7zmUYP2qCb2LJgfXAyWAtWS9SETtr8ondo": "OPENON", - "4MmJVdwYN8LwvbGeCowYjSx7KoEi6BJWg8XXnW4fDDp6": "TBILL", - "cpbqtLWH8DkwuVYvKeVVjV9JowkFd8kkpo1vjCSopen": "OPEN", - "HakUTodhpcA6T379NtPAUFajLykY1yUq4yrnSciWpump": "K403", - "3mXoWmUd3LcWEhtY1vQwuXwRnnhn6h5SQHnXbWqpXopL": "OPL", - "5q9bwAWb25x8m57mwp76UoMazYgs7MYRGDPprJfDawgr": "OPENSOULS", - "Cpzvdx6pppc9TNArsGsqgShCsKC9NCCjA2gtzHvUpump": "SVMAI", - "J1YnyKzmxBwFkPftvZexnHTm4Am7JnmWydhxtXdwEmMv": "OPN", - "6cEeWxTBe6TcE5SsFRAXPUYsid8EbaRDQ1PkoopSkdkT": "OWA", - "XsGtpmjhmC8kyjVSWL4VicGu36ceq9u55PTgF8bhGv6": "OPENX", - "gbHFTMkuMQUy5xrgoCBdaQ2XYvNyjWAYcnRPh9Condo": "OPRAON", - "HmCxqpgxaQng3A73F7vLgJjc9sr6ACFt8CEJwWY2hMGR": "OPIUMBIRD", - "BK5GJDHXs9Nhnn4fcJrTauZ4guii5oEw7Cyxr6Ecpnqc": "OPAI", - "Dxg9cLvssqb1WEMpyynjf17kGRc4UTNyUqjzNj99opus": "OPUS", - "7qQnePEqSRBxnkFQQJ8jUYXUqo7mSkbwuUHj1jz7pump": "OPUS", - "8AYpR27W1Y8WrW7yVyxB8AaM8qCTuCwMcB3HFmsFpump": "OPXL", - "5Gzs2DxqK44fK3ob2LuxdmLfYRyuUe1QeE9MRSM3pump": "ODEGEN", - "GmDADFpfwjfzZq9MfCafMDTS69MgVjtzD7Fd9a4ondo": "ORCLON", - "XsjFwUPiLofddX5cWFHW35GCbXcSu1BCUGfxoQAQjeL": "ORCLX", - "BPYhCMNao2XG4UR771LurHrwUg3rcp76jmg4XFfAacvg": "ORA", - "oraiyuR7hz6h7ApC56mb52CJjPZBB34USTjzaELoaPk": "ORAI", - "B6CdHKLm1XRxxgTnK9cNvT3DA75hPc9WUjTkxVUupump": "ORAMAMA", - "29JU4mA9A66F6GLkdGUwL3rKdBpL8Mw6VZZHb8t8pump": "ORANMAMA", - "GekTNfm84QfyP2GdAHZ5AgACBRd69aNmgA5FDhZupump": "GRIFT", - "3oZnJZAH8EDJyFxA1xv2L1DJY6r54yFsdTbPTpWBpump": "ORBS", - "BGyjasmSzYM9hHiZ1LBU4EJ7KCtRjMSpbN4zTru3W5vf": "ORBT", - "CzywqyWnzACqc7nphTAPHC8cb2h7ou6U9TehBrVnzJKx": "ORC", - "orcaEKTdK7LKz57vaAYr9QeNsVEPfiu6QeMU1kektZE": "ORCA", - "5YSjRtJJTZpQ44YF6qDpyygPCrJ61q5eNJRJRZMvKjZD": "ORCAI", - "CEB5aF8w5hf3W2QVXpFVG3GEdvhLYyHVpiVAuFSYwave": "ORCASM", - "4WtkHh1Gwgd5dc9cfqSY54U2fUZHbNzVLbrmrbvFMGhq": "PALMO", - "4cCjHnKqLFMMNYQD2NYuRMrw7nXkxVsD85G2MkMA44vM": "WBRGE", - "ABt79MkRXUsoHuV2CVQT32YMXQhTparKFjmidQxgiQ6E": "ORDER", - "u9nmK5sQovm6ACVCQbbq8xUMpFqdPSYxdxVwXUX4sjY": "ORDI", - "oreoU2P8bN6jkk3jbaiVxYnG1dCXcYxwhwyK9jSybcp": "ORE", - "DuXugm4oTXrGDopgxgudyhboaf6uUg1GVbJ6jk6qbonk": "ORG", - "G85CQEBqwsoe3qkb5oXXpdZFh7uhYXhDRsQAM4aJuBLV": "ORGO", - "FBaQqJAuYkuKQpAcDuHtuupSPpqi33fbTxKXSjLDQory": "ORY", - "NKaTB5vC3srLog8W7jzdjnejig2iH8eDpkDM31Dosak": "OSAK", - "JCXMR4uW9aGDgqEvzSJcZ88iCtsm8MM2tQqk7P4Ppump": "GOPRO CAT", - "ThwGDsXZ6iKubWuEQjmDxGwF3bUERDGbBXvcbjFondo": "OSCRON", - "2otVNpcHXn9MKeDk3Zby5uanF3s7tki4toaJ3PZcXaUd": "OSOL", - "66MH83n3jRqZMni87niTyKRoGbh9HxBkTm73sTRKpump": "OTCFI", - "i7u4r16TcsJTgq1kAG8opmVZyVnAKBwLKu6ZPMwzxNc": "OUSG", - "7df5p5aoW787CqUWGU2cwtdmtjd5Eiy3oJTbSpeRjupx": "OUTLAW", - "8243mJtEQZSEYh5DBmvHSwrN8tmcYkAuG67CgoT2pump": "OOBE", - "3Ps2gVJpDyEM2NDPgQNWreVUUn45i3HiRFb9rjdXpump": "OVARY", - "8WFe29HGpJ4XH1xQUAq2aFJpMTYEVddiDDytnKPWpump": "OWO", - "CYtqp57NEdyetzbDfxVoJ19MWHvvVCQBL9jfFjXWpump": "OXE", - "3E2z4KX7y457xJqK9RQeJhA29oPdoUvAAD3Ea3zQyuG3": "OX", - "H52iXFyxXJYD9VV6h9cCPk4Le1uA23D8gUKbsoGyjray": "OXLABS", - "z3dn17yLaGMKffVogeFHQ9zWVcXgqgf3PQnDsNs2g6M": "OXY", - "67iVSrbgQKiPjttU9uXx9CXUoS1Si3XUhrXeiNAUUZSR": "OZA", - "PAALhGKVeiWMAatwxnwuvXoHHxYQoftLLhkgmXgFdmM": "PAAL", - "6HsMxxNDee1WsRzonNtEEu35Lti4CXey8wzueY7rpump": "PACE", - "AbK6sxHUV26mqQFTw8HgckKApHyev1ESZsKiRTJcFESB": "$PACO", - "FJjKH9Xp2SvNDNUSN7X9T4uMNafFEYzbZpnwEZXKpump": "BOATKID", - "9tnkusLJaycWpkzojAk5jmxkdkxBHRkFNVSsa7tPUgLb": "WORK", - "1Qf8gESP4i6CFNWerUSDdLKJ9U1LpqTYvjJ2MM4pain": "PAIN", - "6z9XSmYTZffoPUrpuManU7rxjfn7naEbWnDacdAXbonk": "PAIR", - "FvER7SsvY5GqAMawf7Qfb5MnUUmDdbPNPg4nCa4zHoLw": "PAJAMAS", - "HfsnTS5qtdStwec9DfBrunRqnAMYMMz1kjv9Hu9ondo": "PLTRON", - "XsoBhf2ufR8fTyNSjqfU71DYGaE6Z3SUGAidpzriAA4": "PLTRX", - "9eS6ZsnqNJGGKWq8LqZ95YJLZ219oDuJ1qjsLoKcQkmQ": "PALLR", - "M7hVQomhw4Q2D2op3HvBrZjHu9SryjNvD5haEZ1ondo": "PANWON", - "XsQ6NfzzLH8nspjrB9X8R2K64Zz7Tnxqu12juDiMPMW": "PANWX", - "4qQeZ5LwSz6HuupUu8jCtgXyW1mYQcNbFAW1sWZp89HL": "CAKE", - "Aw8qLRHGhMcKq7rxs5XBNCd9oe3BvoAhpNMVz7AdGmty": "PANDA", - "DaUq6WNeLkwjdnWFk3xC6NVXpTXfLotVnUCd4qL9Ling": "LING", - "PANDA8AgjdgQqRRYedrXGKTb2WygNsAhxNDpD7YrBgv": "PANDA", - "7x1eHwdX1eKffNkQwJJtPsHKy6zEKGMuEvmArVHqpump": "PNDR", - "4NGbC4RRrUjS78ooSN53Up7gSg4dGrj6F6dxpMWHbonk": "PANDU", - "FViZQJymuQ9FaxmpudCQS7ffJUt4udHzwJKPBiy7pump": "PANTERAI", - "Fb2DefbdjKFPfmTnq4xnuZ9xdtVyXGsEAetoFmGLZQcc": "PPT", - "EXdq6GgKTNGQiMyW3s61CwV9meTEdPM5Yd1EpdoXBAGS": "PAPER", - "BgV8qdfkjCDLPuDwhDaJGpgJj5XeYJeKwhG7E2bkpump": "PAPERCLIPS", - "GDH5e33UuoJYkCcsyMBJ1VLtezPZ1hZXq6iVW4ADGGKf": "CHULO", - "2adFTbyzVwMwPtgV68DUnrfdwB6tTAJiiVxJ6D8nwiEG": "PAPO", - "9UFoZBPswibT1fWHJBNdBJ4wyKVVpJXNxfdHZnJmoon": "PARAX", - "HrkKngiUavecwte1ZMrdt4H5Qet3cecNUzMoEAgjTAX8": "PAI", - "3ywtR9qQH4BuA7LSfLvEuW1gh6ha7EK18XwLbHvhryPZ": "DIME", - "Hmatmu1ktLbobSvim94mfpZmjL5iiyoM1zidtXJRAdLZ": "PSOL", - "4LLbsb5ReP3yEtYzmXewyGjcir5uXtKFURtaEUVC2AHs": "PRCL", - "Ea5SjE2Y6yvCeW5dYTn7PYMuW5ikXkvbGdcmSnXeaLjS": "PAI", - "7k6cZGzuBjstHqaY2RLZ2h33jWF5oKWR6zHbFpfkt3Re": "PHAT", - "GeUX5oxR86Uge9T81NC7JXdRZ9rBhtbKCMN8WhZPpump": "PARROT", - "pA3VznTTnS22JERJ2okMK3hFebbw8dEpWph6UCf6USA": "PATRIOT", - "PAWSxhjTyNJELywYiYTxCN857utnYmWXu7Q59Vgn6ZQ": "PAWS", - "HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM": "USDP", - "E7NgL19JbN8BhUDgWjkH8MtnbhJoaGaWJqosxZZepump": "PAYAI", - "PAYmo6moDF3Ro3X6bU2jwe2UdBnBhv8YjLgL1j4DxGu": "PAYAI", - "HumaXepHnjaRCpjYTokxY4UtaJcmx41prQ8cxGmFC5fn": "PST", - "59obFNBzyTBGowrkif5uK7ojS58vsuWz3ZCvg6tfZAGw": "PSTUSDC", - "FDgSegoxrdpsct21YVeAbC9dWeTwTxA8Cceeh8BPpump": "PAYLESS", - "9TwMHjLVck28REn4eHFxeqMYKkJBxLqwX8w7rxrdpump": "PAYNET", - "hM7B3UQTTR81mS27SxDDPzBbjejmo8fnpFjzgv9ondo": "PYPLON", - "2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo": "PYUSD", - "XshWQWYVp5ff8CrAEsGmLVKD47nBWi3Ygn5v8wXK27G": "PYPLX", - "pawSXHWsonrTey4SX7tz1fM9ksuLpE13Y54K57ym4Rg": "PAW", - "PAYZP1W3UmdEsNLJwmH61TNqACYJTvhXy8SCN4Tmeta": "PAYS", - "PnjETBCLC318DRejo9cMQKAmET9PvW8AEFGWMNtondo": "PDDON", - "J5XxXQueFp1Har8GYa69N8Jw9yAG6r9VKujhAFaTpump": "PEACE", - "85vdovHhkXnDi98EYMQmD2vXS82jRP1VDDXfkJ38pump": "PEACEGUY", - "3mFKFGsc2NMAYrcGPmTgemPM2c3hemXAcH2k1kntpump": "PEAK", - "6N1myX27SLSnb4fnArFAF3yNc98VXCUopZAqyzExpump": "PIMP", - "dekNoN3D8mXa4JHLwTbVXz8aPAyJUkk443UjcSpJKi4": "PEANIE", - "2qEHjDLDLbuBgRYvsxhc5D6uDWAivNFZGan56P1tpump": "PNUT", - "J6dbiL3MfjnkgWkVqz9nrEwVZYLTC3d9ySSt3ensDXKw": "PEBLO", - "9SiKU8vnRiBYQSBff84K5zwG7habzwYVzn7KrtgCzNfg": "PEDRO", - "8ci29P8UHBaQVX752CRhD3btuqDh4mA4Mzqb1Ucjpump": "QWACK", - "CmXr8rZyqxbFKv44kk1u8ixQM9mZnUf625k56p27pump": "PPPP", - "n54ZwXEcLnc3o7zK48nhrLV4KTU5wWD4iq7Gvdt5tik": "$PEEP", - "BqP9K6DMAG5hncqMY6uUWuFKzxwwyW4zYF3Ahuf3SZCT": "PGRE", - "G4ZgRBCFYMEm3JmFRg29epeTTWrWt8kbhjgsY8BFpump": "PEGECOIN", - "H4PDo8ngWwC4quPTRWfTr2HorUQ2Ep4G3JVeJHMfkZAT": "PEIPEI", - "F4gxUGrn1xEfRgJj93BqXBqz9ZacR2TzxqfztiMXpump": "PEKO", - "31eijQgD7tQ9TQoQWvcbGsYQmb2rjrAt24UacBDXpump": "PEKO", - "AcNia1KNYeaDAuswtpJJZpPqzpuFNyvjd7LbxMXSShji": "PEKONG", - "BgJW7U1u2RY5XJk9uYb5AqFRzjMtqE7pw3kaf9iw9Ntz": "PELF", - "SWKT3Sksvtcepkn7MRn74WPUNH1Lo8H25MAogVepump": "PELICAN", - "A3eME5CetyZPBoWbRUwY3tSe25S6tb18ba9ZPbWk9eFJ": "PENG", - "5gdmkH7fRf2cHeX8AtrmRS75s87gbnDd4SpXHiVcpump": "PENGID", - "PENGEKyPYXYDnbXGKcjXaSfMsovhcrtPT8S7127tKcg": "PENGU", - "4M4ypzyZA7Sp2weYEMfnWuJ6wBVpNaMgaGTanmTspump": "PT", - "BHtY4w1m4gHLynDxx6RwmU75bNmBadfVcVcQmyWopump": "WIFBAG", - "3JgHze5iM3ztccvgrnvVaCAjPVqWk4hMssyZXn2KJ5S2": "POLLY", - "9YHZjCb14bB9XWZz6FZPTf5nek7kLeoGHmNNrgcxpump": "PENGY", - "B8vV6An7xFF3bARB1cmU7TMfKNjjes2WvY7jWqiRc6K6": "POS", - "6Jj5vp8sMzj2SRAVRN9ZS2bqBUYDCvPxLGShXKMHpump": "PENNYSTOCK", - "FCPMdpvwxtWBvgDDsPuBb6wyf41ik2gtPqNN9jWSBAGS": "PPW", - "KyxLLGRvJZDRoAFS6PZ8geq5WNzY1b4nCwsokYJtYms": "PIE", - "7k3j3ZsQvwB3eWXMRVMCXGzp9U42oKFLCFnLtUWVh1iq": "1IQ", - "2wqkw3idoEYFV8fyM54PvS5BYHieT8rAZqzdHg6aqZby": "PEPEAI", - "BWKyFgvQSfjNRbidRJ5CtuphashZNr3ba7oe9cjZpump": "PEPE", - "6Neft4334Hqi2ak59K3SActWD3jWuhLvXuFHhGTpump": "PUNDLE", - "CRAMvzDsSpXYsFpcoDr6vFLJMBeftez1E7277xwPpump": "PEPECAT", - "EXJvx3KksbWP9QmPmtRr8mkQXD2kZrFRENCJitMs1eZ6": "PEPECOIN", - "BKmYE8w1GEQ6khhLkvMj4ULvV4Gq7zLfJZSgsHE5pump": "PEP", - "3tS6fbLh2P8tzxXuqCiHZpZhsxJpmrR3Xb9psmypnp69": "PEPE", - "8jzRznet3cR3Y1KhciwUxKEqPku3BLcfiJLpVjMU3NF7": "PEPE", - "DHsPzhYsz9219sQDj6vYtQMBBHeuyXAM1RA4mjv2V9AT": "PEPE", - "tRmRf5Cycf1haB3p1kekwEQa8Jx4oTyzJf9oXVupump": "BOSS", - "846XGqAnEhKFwc3QL2e5PQhSRvEbn9KspuxJ3nhY92kh": "PEPE", - "2yd2Suus3YY4Sa7LHhn1PSHkjXj3XKrars4cCog2tGU8": "$INA", - "FyJvH1WvCPPvhZaH8F46DAHdgMKvgKSvDRXF6A4Lp1Uv": "ƎԀƎԀ", - "2FJkXdneMLkESaoU2U4nC1y5VNNFSxpRY4EJZwLpepek": "PEPEK", - "Gpg9shYtjVgJ8LtMye3tS6izM6a2vK8Eh7XSEwUTpump": "PEPELOCO", - "F6EzAYsWQb53Zr2VEiU3EefCwydmhkJ5pn2RNxUJ6mGf": "PEPEMAGA", - "E1THYtEMfKb1RSPQG8d2iq31JQaGN8Jh2j88npwUeVqU": "PMUSH", - "2mmdiGEDjiKgG2Tefj5UQad7qUT4eEqLDeja2ixipump": "PODGE", - "PFireKhT5WG7axMSLBmMRpvYH7cgHx9CRWHU8F8HNbr": "PFIRE", - "EdNiGWbkBwmmz7uhtdY3DqKZn1n5m1c61uLVgvXYZWGS": "PEPE", - "FchXquY7jRGm85eaSoMtsJuUFZJPQEvcW2sC3AH6pump": "PAT", - "3X36yhq35MJnt2JjwodeFDfv2MFPb99RC53yUyNrpump": "SKULL", - "F9CpWoyeBJfoRB8f2pBe2ZNPbPsEE76mWZWme3StsvHK": "PEPE", - "B5WTLaRwaUQpKk7ir1wniNB6m5o8GgMrimhKMYan2R6B": "PEPE", - "FDqp7ioPenKRzQqseFv84kxDMUT83CX1qZxgDTQDkwT2": "PEPE", - "6b7NtVRo6jDSUZ75qfhHgpy99XVkSu1kfoTFu2E3pump": "PRAWN", - "9kYJGVYTxYUVPw59hcnXG2Q6VsC1GbK4KkhHsdrDpump": "PEPETOPIA", - "Gk2kRrwNMBU4Dn9JhC1Dks8G5X9nqi4ZE5jMvK6bdgEd": "PTRUMP", - "qKxNiXm85xhS2QBQBg3u43sf7E4mUDY73LL8N83pump": "PVRSE", - "2qachMY68G2oPXt2SYPVSu3SyR1YgiiZFZ1pjJxnxKyi": "PWC", - "8vRnnknwERunJEEDtEFoxzaxbxGnEY61FPbx8uTVKsUD": "PIF", - "7So8nmhmAMNz7Sc618DPkpJaAy9VnfFvdy6WiBA27PnD": "PWH", - "EAvKa9kGRMcQ44Eg3MPbXUP8ZEpfxgd5dToHBRF8m8ui": "PEPEWIFHAT", - "D1ySHVWnaWQsf8WiskayoF7oHuvXLp4CXvYw3PaS8N7B": "PEPITO", - "pepo1CFNU2RXf7yXX7HNXazXwxsq8WrPvDHpHriwoLY": "PEPO", - "Gqqdgfkn7bcsuBQZEk9oMBkqCv1bRXvPmTLs3sQ9pump": "PEPONK", - "zRSBtynHT8gbBBmTfQBAEWMQiUorwwAtng7YFBfpump": "PEPPA", - "gud6b3fYekjhMG5F818BALwbg2vt4JKoow59Md9ondo": "PEPON", - "Xsv99frTRUeornyvCfvhnDesQDWuvns1M852Pez91vF": "PEPX", - "PERLEQKUNUp1dgFZ8EvyXHdN9d6ZQqfGxALDvfs6pDs": "PRL", - "D7t47PKLFBM5smm9LYrDpEuqJ4C2PBAvWLhit5hhpump": "PERPCOIN", - "FWoGYRhR6YagBSqncPSdn43GHNikSPw1ggrGzyM3H5rE": "COW", - "CXobgfkQT6wCysehb3abkuimkmx5chS62fZew9NBpump": "PERC", - "5UCNsNY2oHDB7i2m8bW7nKJXxRoh5WS4N4xKmUB2pump": "PERP", - "5QvAeS2Un6ZoNjyKthGYo2ZpbcVP3diTMejyA48NEHTE": "PERSOA", - "5LwseQRo8fsz4S3y7jbqqe5C7tZTz5PwhXNCHj13jLBi": "PESHI", - "34a8ALsPmbWxp7D3bQ6erERrCLz1ahr6u6o66Udmpump": "PESTO", - "GDmSzyg3F3CqgusT4CcxhjaKKfaucN98L4EUePeqpump": "PETAH", - "GRciFCqJ5y2hbiD6U5mGkohY65BZTXGuGUrCqf7ondo": "PBRON", - "26HijyQRPisA96er6xWLh32TSMAHLvivTnmcmu7bpump": "XPD", - "F2k82EcxLtzekq1bfoGVdgp6EXZ5dLT1jE7g3LvQpump": "PENGO", - "e9tZR9VJXqpjbGGjBuf3ypAgaf4w3K1p22pDfTTMDTc": "$PETSNA", - "8NrAzg558TYtwdGYcwomsLHfdQ5dwEYckXqgfL6tpump": "PETUNIA", - "7tPPYTBKrFLKKnoCwijrsfjAYadyp7GpAmSPUbVwbonk": "PETUNIA", - "5zgTYTDK836G2Fc4ZLQp4rsyi78pAbuDr4qaQUE1pump": "PEW", - "2kPoRJf9DTvcJPDBr1os49pGC1X3qSbJDLYPw12Rmoon": "PEWPEW", - "Gwh9fPsX1qWATXy63vNaJnAFfwebWQtZaVmPko6ondo": "PFEON", - "XsAtbqkAP1HJxy7hFDeq7ok6yM43DQ9mQ1Rh861X8rw": "PFEX", - "UP5s1srLaHDc4SwJqLPa3A48x5R7ofN3hZWxWEZondo": "PCGON", - "pSo1f9nQXWgXibFtKf7NWYxb5enAM4qfP6UJSiXRQfL": "PSOL", - "8fJCtfbuAaFzdwTnQP84nNhSpZdVy4Uc929eXZRBN8H4": "PHRZ", - "5veVHPNDsmSxDW4Abpb368wLHpo32LP3fqhfJHnkSSwo": "PHAUNTEM", - "BDmd5acf2CyydhnH6vjrCrCEY1ixHzRRK9HToPiKPdcS": "PHDKITTY", - "BxVQV55MSNF48H3Lur4fj9CTEA7bCVmFJg7gfym1Gs5M": "PHI", - "Xsba6tUnSjDae2VcopDB6FGGDaxRrewFCDa5hKn5vT3": "PMX", - "41xx693uzU3FH7vmq2erpqc4kZWjwhSntJwAhZGRpump": "PHILL", - "Fss7oX43YpNNWVnRZugZ1NcngR1j8Ar33Z2dLqYfpump": "RAPTOR", - "C19J3fcXX9otmTjPuGNdZMQdfRG6SRhbnJv8EJnRpump": "PHI", - "DhRQWsiP53gVXnG8KQwq63Uo1FvouRpVNL8536tsVD5H": "PHTEVE", - "9YnfbEaXPaPmoXnKZFmNH8hzcLyjbRf56MQP7oqGpump": "GOLD", - "EswgBj2hZKdgovX2ihWSUDnuBg9VNbGmSGoH5yjNsPRa": "PHY", - "Eppcp4FhG6wmaRno3omWWvKsZHbzucVLR316SdXopump": "PIBBLE", - "DAfKg6exaeMMUQeqwRq3UP5KYPJjFNyVuVzFfr4kpump": "PICKLE", - "PC1ZhGPajd4Xo1G8NNnZ3gpUtSgJUWps6kKuvMhkPBN": "PIG", - "GB8KtQfMChhYrCYtd5PoAB42kAdkHnuyAincSSmFpump": "PIGEON", - "Gv7sW9pe9khuPjCYmRUTmHNAFm8yWDJVtDVyA1DExqr4": "PIGEON", - "7y1gCXgTJQz6XELJayX97ovtYv7PGsyqmLtotwSAbonk": "MOCK", - "676YgDtdAekpjYwNvLSLFPkBooVxBqJVpgxxoHJPpump": "PIGWIF", - "Gfvayte2S8QofiRsSXhdX1XX1CbcXvZP9jJkAU5Ypump": "PIK", - "A18GrBLPSWUGg1pp3tg9oJU2KBQrkkKiyykL21b4u22i": "ORBIO", - "97Mbx6Jym1iEkQdVfLf5PMWL5tARGnQ5na5jhR9Lpump": "PILLZUMI", - "CsN1Tyz467bSFLPGd6MJyZhPNtwDaWZtX8ixHWyondo": "HYSON", - "zrvL3iuAqoBdfwAmaUYrYcS8UjUk6LbKrexT7aepump": "PIM", - "14GjpGfdm35R6PP6CVuZvtRMpVrKX8nJhH7jHk7fdrug": "PNL", - "pi1RgmNaLQsNEyEAsrEjgmemojPwitwDAXc3zgseWWF": "PIDOG", - "2idBzH45gfXFEhivcQVTXVgQGUDhX8scXoZwAiTQUT6L": "PINGU", - "Ffjrfw9phxYYpQc9fyYq5uRV3K6943Wzo3t9a1L4vfoW": "PINGU", - "7b36cKRYFZsMp3vLByVwfVQxW2ndcYth5rhPnyypump": "PINO", - "sxyg1VTSzy5zYANUK7hntNtmFAWoXGJq95AcHuVondo": "PINSON", - "v1G66wZM51Kjp5YFw3mws5MyQGK2ry5MHRrArZipump": "PINTO", - "HHjoYwUp5aU6pnrvN4s2pwEErwXNZKhxKGYjRJMoBjLw": "PIP", - "6d8p12ENyoNmpZ7JXNWHmnLwziNjzXov1JKf327G2FTp": "PIP", - "WYHYGzvnz5xWs3DzjasXucDppQmA8W5QVnpk38sd8pp": "PIPCAT", - "3T721bpRc5FNY84W36vWffxoKs4FLXhBpSaqwUCRpump": "PIPE", - "AX8b9A79uDBTKDQoWTzq3NaD1FN4JEDGKR2yTQGhcwjo": "PIPEIQ", - "7s9MoSt7VV1J3jVNnw2AyocsQDBdCkPYz5apQDPKy9i5": "PIPE", - "BZKuDqzD4rb2puUV2cbSNd2kZzJ5JzKiQKwBzCk8hrLu": "PIPI", - "RodhH2Xivnpt9AjK1ZniRap6TTcK1yx9CJmZ5zqPipo": "PIPO", - "Dfh5DzRgSvvCFDoYc2ciTkMrbDfRKybA4SoFbPmApump": "PIPPIN", - "AbP5LEN4sAAjghBy6ww37ngAAT7hnBVfXmXNdCnbpump": "DOT", - "6ydMmFRaNt4AHBjgvSogbBvcxnTrX3QzUe2AEzVppump": "PIPPKIN", - "3JsSsmGzjWDNe9XCw2L9vznC5JU9wSqQeB6ns5pAkPeE": "PISCES", - "5EqRDhtdM5We4a5oyLeebJz7LfsRyXMNC52DMNEhpump": "PISSCOIN", - "y1k9ZRqLwKLbyUe5LfQf53jYXeSyvrFi7URt4FWpump": "PDF", - "BiwQX15cvwpyyGa5SC8uEXCqE94yXKFuLFSj5WtBbonk": "PITAYA", - "Hc5oUkbMAmLHRmyxzxNkkVhsqSAn5A9LEjcCqZH7pump": "$BULL", - "pxPdoEVJADUP5WD6d7vRQbwZknEzUL7pcNMJmXjpump": "PITH", - "Di4B2JSRykk27QcD9oe9sjqff1kTW4mf23bfDePwEKLu": "PXL", - "3FeFKTFQk2pEWwqax7vjxz6jSbFUpPASnACRSzDQcSRQ": "PIXEL", - "FtHCi9cxJSSizrzMzsPjAfTfJi32V1CGRDM5Skqn4QBF": "PIXI", - "3bGuPexrqNdmXkSxeUuMavYDFdjpQTSDt9ZusDgDpump": "PIX", - "EwMCa7DgUEGhVmFP5ubTsfSNh3sgJQA4n6dh2Apepump": "PIXTEL", - "4AkCN6KLeCmUDjWLg4XyQpuZuWtwBdPcbtBQjsA2pump": "PIZZA", - "AyCao1LJJKrabYw4ZRLtXTtUiWxE2M34Eqi4Xkx3pump": "PJN", - "XsMJtFbb8BwzQtck3oRXyAfs7SAPRuTXnSEDNd7BAVz": "PLX", - "D3QiRT12vKBpj87h99ufQFz4mCpbPC7JVy1U6NRKpump": "PLANKTON", - "7Rtegu95FZZ2KX3zA37LU1ypicxu6pBysNEagFwhpump": "AAAHHM", - "DVuaDuQdPZ6H49inC2Xoyx7BpLAAJTPPChSfHuGpy8X4": "PLANK", - "4NWHUsqene63s56idLAchHC85MgvCucBgCsMjkiZpump": "PLANTFUN", - "DU7BqqKt7vC9i7paUQSeduxkWkTHQmoVR85M79pfpump": "$PLASTIC", - "7wMutxpCdP3GbKaoMWoPVf2sFLCeV7a9ox25v1h2QsTp": "$PLATH", - "EtTQ2QRyf33bd6B2uk7nm1nkinrdGKza66EGdjEY4s7o": "PPLTR", - "DeoP2swMNa9d4SGcQkR82j4RYYeNhDjcTCwyzEhKwfAf": "DEO", - "7yF97k6jrBkb7BXJYXzmwQLQyxVirLtCSSGb75qLqAN8": "PYM", - "49UseCTadQcTQNeqpKJHbZgBXRSiLGuJZRfePQkTbonk": "PLAI", - "PLAYs3GSSadH2q2JLS7djp7yzeT75NK78XgrE5YLrfq": "PLAY", - "ADVgDMbbn6LZTdo84UDAgCUew2JMMHe6BnH3KNN3qiLb": "PZP", - "6PaVHw2HR6ybTvPMXkLcGUF2A6Hiy857k8PQXkTnpump": "PLEB", - "1dHeFbmsVktbHXGcW2GZkfkfYxV4yTKbcusNV3fyTX8": "PLENA", - "9kCuaGMx6GNBcTfZHEJquzjZaayVThVLPzoQ16SYNj2c": "PLINK", - "49Jy3P5J41zkcCgaveKXQfeUU3zNCHCSEEypkJTrpump": "PLINC", - "ydDccyq66xKtfqn5bsRpfFXz4WeF4fh3bgQBx1npump": "PLOI", - "TnfswqdE1jAJ8sfnf5J7kSVLEH1cfpAYZ8MWmKfondo": "PLUGON", - "2cJgFtnqjaoiu9fKVX3fny4Z4pRzuaqfJ3PBTMk2D9ur": "PLD", - "pc3gLpoZCe79SZAbABtes2fiWAaiTJuTk9NsNxR2ZSj": "$PM", - "AX9D7Nqtu3enaeL4ELxoagsNV7AyEQEhK2ziwiyJpump": "PMPR", - "58mWi7CzpsRjrDsv61BN1ynpNLoGHUbxAUeec141pump": ".PNG", - "UVcu7kbVKW6Rs5PKzuwVSbm8ukvrWByS3hBXqHapump": "PNP", - "7jYfnjn3jHWmUhhgfkZ9uUEKroH3Zz1BvF8RmVQHm86D": "PNUT", - "BDbhJUMgQBQ1bJTKhSS9NsxZdfaiBG6Hgp8qNKpc3YSX": "TUCKER", - "8g32ps7JWAL7EDkmBhW49dkUCHRq1Des1Hg8sPSb3ou3": "CHLOE", - "2kkwPP6Y5v4aSteLo2R6uqmx3aQ6Veb1xDcRXHsNbonk": "PNUT", - "AAmgySbEeNWyn1JdKqJbdKAqa8JEtecVZamaNvkt8Ccw": "POCHITA", - "6LH9NDBYUf7thDx8sMZppZpxAsdXtsQ9VN37gKMpZRSp": "PKM", - "6CAsXfiCXZfP8APCG6Vma2DFMindopxiqYQN4LSQfhoC": "POKT", - "6gVJwfHxWov8HxdAZ4v4tt2RpZZst6du74y4bUCdpump": "POG", - "2miHpQbYLPvXxC2V234jrrrtnJdejd5xW883a5ToNW3g": "POGAI", - "93x67YtArpqdzmmyXQ7qxhvvoDTkC59dsHWXhD7Ypump": "POG", - "3ywqi49rbrsgyNoyBDngQKKjqFTMxCrZXF2NgnYDqGo4": "$POGGERS", - "BFUadco9hktp8wrLas34DL5jmVYE17qYmfxZykV7pump": "PJ", - "Gg1wPd1JybEYyTDrT5N4pA2YX1pTq7tcpR3CEUuZbonk": "POKERPUG", - "4v2DRssbwaMbdMwqqN5EkSB9dCkKLorcg3ht9sHEpump": "POKI", - "GMBA3jpN1pzkQfFh8RSQ1rCT9qCaDLYMF51mtbWMpump": "POLAR", - "3zjV4gHs3d2saVjSc82hEnxaBwkRe2timgP1onNNXray": "POLICE", - "DuhSwRVN7z8bWjYzwtRv2uDfpAsbTPxDnDezzi9Nsf1y": "POCAT", - "D5WJ9eH4WLF1SUazsMZLUKTz4xb6mKHNzS1ufeynmoon": "TICKL", - "6GihmsqNR21azuRuHgbjQxcDoxX7xgqqs58PQkyxpump": "POLLY", - "5eMfXSYdssCpnu63WtPprjbbR5YBJmSEnZGRvtuppump": "POLY", - "FgHpSsku7asGyTThooDmkKwmzy24ucUeyBrW9kLqpump": "POLYAGENT", - "J27UYHX5oeaG1YbUGQc8BmJySXDjNWChdGB2Pi2TMDAq": "PCULE", - "FfixAeHevSKBZWoXPTbLk4U4X9piqvzGKvQaFo3cpump": "POLYFACTS", - "CWR9o6fei1J92MhSoKqDootrhBo5zDgjsqeQecSzpump": "POLY", - "Pre8AREmFPtoJFT8mQSXQLh56cwJmM7CFDRuoGBZiUP": "POLYMARKET", - "3pDjFXDsYrPEt8B8SVrJDa1FZ7Q57CJx7uxeNsCVpump": "POLYMASK", - "HygZx5u3aaXg38grfa39Y59NSGFQXSMCUyAYWoZfpump": "POLYMI", - "3BWA5RBXyPXuMGZmVL8Snefu573FMJNGpsVi79baiBLV": "PM", - "2k5o37XM59vQRmnnVnJAEspqfEG9VfoFNTywH49Jpump": "POLYPORT", - "FsPxXisr7W2mSipE62hXPeVMycDKrV2R1Jf7RQHBBAGS": "PP", - "AigqqAyxV1quPYjskhpoYEK3szVRDKcHmiaFj3rXpump": "POLYPUMP", - "5FfhakAvaXPJNR7wDwvB2UbWRY6KrsdDxxCFB7uKcBLV": "SPORTS", - "BuWe9Wd5XqCUUV1xZMtcpCPg7iqdYoyC8dBdSbEUpump": "SWIPE", - "3gfm611yupHrsZ34hc5K87Q6pWXx6zLwL481hQq3rGMA": "POMATO", - "Dkm51nnSbJeet5fCVKgCCMiXGveLH2k6scGVh5DNTxQN": "POM", - "3h1H6gNEGRsKCV76FDJTQaN9g1LNkGPPX3XmKdispump": "KORI", - "HeqCcMjmuV5s25J49YiJyT6bD5qWLkP88YPajBySniaV": "PONK", - "2GMEDWxPhdBicySMjUky49UHgXutxQ8SJjWyrcKPpump": "PONK", - "5z3EqYQo9HiCEs3R84RCDMu2n7anpDMxRhdK8PSWmrRC": "PONKE", - "56sDqbDNaRwP6HK1o9wpYtzE5PAkNePsQe52oPx1pump": "PONKEI", - "9ZoH3ssTQBJNVidsLo49q3buDidreiZc9g3NeETP3F2s": "PONZI", - "L84NryNjZWNDmBDVy5bYNGwgyxxoFExZmo3Qnohderp": "PONZILIO", - "2d6UM4ocm5zENYsviZwYWRuNduYd21mWx1QKf3Z824aN": "POOP", - "J8XFfJgS1cs5WVGxGwZb9Rt5rJZizdyzaLhidtvDnYjC": "POOH", - "AmXoXkTmLvwoQSVqgRnEXA5mLg7f5D7gpJ4wJVGCpump": "$POOKU", - "9x4ZSZhBPzv3xJ2QeWwqvfJb6r8CYZ1R2tNNVHoYbunt": "POOM", - "7uwNJaMRj9FZuubmzguU8A4cR15ykpBymhAiVJEFTjZ5": "POOP", - "2KiHzSXdnenDoDNsVsjU6VcvgyyDK27iSoZv6TNDpump": "POOP", - "7GCihgDB8fe6KNjn2MYtkzZcRjQy3t9GHdC8uHYmW2hr": "POPCAT", - "JYPzbZpgYgit2mtfU5cmcuhWJaB7KTdtfBvfJrepump": "POPWIF", - "Ee4n93iv9vtY8cYcUEqFCpBtmCq1ZMKR3pLzPQsmDMY5": "PCORN", - "EATGZHJViJsk7nEKkrdJicwNbfpkJfAtmrEmrjXR8NBj": "POPDOG", - "C5gnRtcm1a2aCBbtfAPqnDjpQ2YdD9PcfBk8VM2iguXS": "POPDOG", - "DA1qLpgD1M7TNNRPycizyQCoRLCt7GAx1YTeVrfYpump": "POPFROG", - "CoseJDEHxsfjjQs5CaXWGd4PH2irHam2FxeuwN2jyAL9": "POPFROG", - "FySqgkyoZXVYbtFM2DHSjzSHj8e2h1TcguGeiPDboop": "POPI", - "6waeBwy3stLMvxSeA6FjMun8p4TYGre7wpxtyEzCpump": "POPNUT", - "6kJhG826LGowg7zG6PLd6tg7mqvVSdq2WzHhsfc7pump": "POPO", - "96QoNkZLTKH7Gecf6dKMRXBQXW1wqh378uF9pnTejgAw": "$POPO", - "GD1AR5uHytu7nHJ9zWYEEHytmLe7MaD8wg6Tzesdpump": "POPPY", - "8nez2xunQ1JTnVETFr8YdRBV32JRrjsSMzbJ9qFkpump": "POPSEAL", - "GtXFYBrS8mobiQpkJpdhL6fBJg1NJhFUMGCPuFrSpump": "PPP", - "FMQjDvT1GztVxdvYgMBEde4L54fftFGx9m5GmbqeJGM5": "PORTAL", - "PRTLSwfLzpVGSAQiUfXEenJkq1cwTsEcsn1hPL9zwwg": "PORTALS", - "PoRTjZMPXb9T7dyU7tpLEZRQj7e6ssfAE62j2oQuc6y": "PORT", - "JCV8BB8Zde8ukroWKTjLSENSWEj42kBkENjjws2Rh6vP": "POSEIDON", - "EMKP9FV5wakvyodPh8edQW4RTSbHPrhYRDEiCiz1pump": "POS", - "9gr84rGyLvVasmqXvj65njjUgKyBemKWSJsvijTPmcQz": "POTATO", - "PotzPaFGzK3cbu1u3g5HtoZbyTUkyhka8T2aBy8LMEq": "POT", - "5EQotG3SrMgC1pEqEgkPGGVCK1mHWVBFWV7MnKivvPTT": "PTTO", - "PouWaap6bHZv1oT7T7rzRgQHTBD4nsWQRHGbB5hfDA6": "POU", - "BALLrveijbhu42QaS2XW1pRBYfMji73bGeYJghUvQs6y": "BALL", - "pWrSoLAhue6jUxUkbWgmEy5rD9VJzkFmvfTDV5KgNuu": "PWRSOL", - "8CkiSHHJDHJV4LUoiRMLUhqG58cUkbyJRtcP4Z3mCXNf": "POWSCHE", - "5A3TS1kbq8J1ZagEo5RYC2MZsN57eN9mrL1CwqS7pump": "POZI", - "AW8u4LiMo1SKcGiMBPYzHcWynirZYQA5KwsoFUMJpump": "PRAIST", - "AaRLHCvt6G8e3dbxLvN6BQi2S34Lvv3eeBjKNzc2QZB9": "PREDIC", - "57KoEZXm2mJwFqbB7fvcgZmmjc9mivFmKhXA45H3pump": "PREGUNTALE", - "32hUYG6DVZ92N9N839D2Hc5bUWGQTSVdYUPNBHNLpump": "PRERICH", - "Dob5TVKdisbwk1QSppADF8knxHtLR6DovxHGqaocpump": "PRICH", - "FaZwrtepqwBf2TMHVaxRWt4U3Zjv4LxmcisQXYy1pump": "PRESS", - "AFk1RUr18RCFjhKHQN7ufxPBiQYWjXifAw4y4n44jups": "PRICELESS", - "6zoshtkmyX4kRFg3p152yV2bPssxeYdNvW3c6EVCE4UP": "PRICK", - "9BZJXtmfPpkHnM57gHEx5Pc9oQhLhJtr4mhCsNtttTts": "APES", - "2DfBjrPFZjDTiCY6pxchS6aSdUdEpkm7PdqpovHjBAGS": "PRIMIS", - "GaZ6En5JWWR7u7dqnpvRr5tk56XRV3KRtZwdeuR3pump": "PNET", - "28a2oQVCPrJB7SLZo4DVMVdHchSXiG8sZy31DohPpump": "PRINT", - "CsY1MqwwgyBGUrAZq6pU4n9SCQJn6nN5Bo8s8Xwpump": "PRINT", - "8stssUiCFbcB2LqCS62EuU2x1K2NWbJJo1YPgskpftWK": "PRINT", - "PRSMNsEPqhGVCH1TtWiJqPjJyh2cKrLostPZTNy1o5x": "PRISM", - "H5hygVvXiYxk2a3BVtjiqcDJK8TdHTB5u5U1fXEuBAGS": "PRIVACY", - "7HcZz4segTW8rLM6m5bR7ZQ8KamTm1MJ3jhdDNirpump": "USDP", - "BUUB7DpQT1mcTrs55oXawgEbxm5khAozsbmyhMdRpump": "CINO", - "yhTd5ay9wnYWU5Pf6YncgEPA7ybpnqhdLT9poqhchat": "PRVT", - "DrnF17MbiKXu7gVyfL13UydVvhFTSM7DDWN3Ui8npump": "PP", - "GSgxY6HdcxNfLXJqVC8JBhYCKb9rRAdqGfoxZvYapump": "PVX", - "prmmgF5GJCSDNEcwZe2HWb5DsDsFngxTetZ95C4VKxX": "PRM", - "4TUNzcgp2fPD48fcW4seRjyqyDZMrPj4ZubnXFEsKeYk": "PRNT", - "9N9V585yTpmosZacAcXLZWxKJEK7PbaH4RJ8gEKLD9sc": "PROBITY", - "GZ8v4NdSG7CTRZqHMgNsTPRULeVi8CpdWd9wZY8ondo": "PGON", - "XsYdjDjNUygZ7yGKfQaB6TxLh2gC6RRjzLtLAGJrhzV": "PGX", - "HiaZB4rrRXY2pqpHqEfhiGAY1XoMjLRrJoHCcEku3232": "32", - "Bz4MhmVRQENiCou7ZpJ575wpjNFjBjVBSiVhuNg1pump": "PROJECT89", - "CJjraBaedPnKzjnag12i7PVo5pSP6cB9heV8zr89bonk": "PROJECT89", - "72XUGRRzuSoLRch3QPpSPHkuZ8F58rvtCNF4QSosLb4H": "AEON", - "dJhBorriSsJwkWKTFGNDnvmQm7CG81WksStqaYFepag": "ASCEND", - "5evN2exivZXJfLaA1KhHfiJKWfwH8znqyH36w1SFz89Y": "MIRAI", - "76PsEyML7UV9uiBDWMdG3itRRuupDuRs6nNpjNBpump": "PPCOIN", - "9jpeicbKQKiczgu1wBVTDUgM8rVjbQGG2jBK8g8Hpump": "PROME", - "9NW7fiBu4uHpLx3rxiMccucyKwADwuptTpb8z2YYj9SH": "PROMPT", - "3ViHjF8Gy7qXyXTVRfgeQosSuZCRJAs8kdHF475Fpump": "HIM", - "AkbvXVZPzrHSpcxNADQ7Uxk6nvjHtpHn4nqNQZY8TJBn": "PPX", - "qKtU9A7ij34XmtxaSzYfxCpkgAZzzFsqnUb2kW2ondo": "PSQON", - "14W1itEkV7k1W819mLSknFTaMmkCtPokbF2tRkPUondo": "TQQQON", - "D1tu7Fnm3cCpKyyPXrqm5GXShPqMj7a2SEjjq9fondo": "SQQQON", - "CWFa2nxUMf5d1WwKtG9FS9kjUKGwKXWSjH8hFdWspump": "PRO", - "3jMwPagCmBfHezz6jasepJTeFiJcdEt47hJscg4epump": "PRSM", - "4VwJLikGQ5N59BRzxyaZZjLiB38r6HF4mJ1mU8fjbonk": "PRYS", - "8gFpUK3KTjCHhTMsCvkJ8E8mQ5jP3uqpHc65rjtHpump": "GRAIL", - "uPm9ebhrGDnNXiyneeP9WG57wNbQ8bd26DRiWSNpump": "PSEUDO", - "pSoL47GE52V2bgUUyQvs9LSdWQZsokarp2yNsWQaLYy": "PSOL", - "DAFdakKAEvB6GtJgnDk1LC1QK4gQrtTK3eKUZtAJpump": "PSYOP", - "2nP9yKQNSGQy851iyawDvBkzkK2R2aqKArQCKc2gpump": "PSYOPANIME", - "DAAXmRU9KUEuyqF8VuddbcL5X4QhQ3tTDMYxDbNgpump": "PSYCARTOON", - "EFkiR1hE91BkqbxA3YxvYuvDxXTTEMea3zQs8ndbyTQY": "PSYOP", - "E5BuPQkFkGMAvzB6fQCJyGZDVHkdNaYZuj1Kfz92pump": "PSYOPPEPE", - "2zMMhcVQEXDtdE6vsFS7S7D5oUodfJHE8vd1gnBouauv": "PENGU", - "G9tt98aYSznRk7jWsfuz9FnTdokxS6Brohdo9hSmjTRB": "PUFF", - "D9paufRCehpk2YGRPkzJPEUDsvQjaTFKNyFDqgd4xqd": "PUFFY", - "H4zej4MPNaxbLdD18yAkhWMPNYTB9FDve46mKV82kmuE": "PUGG", - "PuLSHCKeLRxaB4qCdwZAoZFj297oTSgVh8SzYZtUfQP": "PULSE", - "7LvJrVmmY7BYPA76EzATeRerjKbsfNUxkRzEtiJ4pump": "$PULSR", - "2FWWHi5NLVj6oXkAyWAtqjBZ6CNRCX8QM8yUtRVNpump": "PUMPAD", - "7vsKatZ8BAKXXb16ZZMJyg9X3iLn8Zpq4yBPg8mWBLMd": "PUMPAI", - "F5QMUBvNupauQScbyhc6WNcmMGRdw1bM3a6oAtxspump": "PBANK", - "Eg2ymQ2aQqjMcibnmTt8erC6Tvk9PVpJZCxvVPJz2agu": "PUMPCADE", - "pumpCmXqMfrsAkQ5r49WcJnRayYRqmXz6ae8H7H9Dfn": "PUMP", - "5TfqNKZbn9AnNtzq8bbkyhKgcPGTfNDc9wNzFrTBpump": "PFP", - "H9G3pEdKuRdyAg7ytxexJ1o4nTnyem4JkC1w6AsSpump": "PUPPET", - "Vrb3u5yd9R9X8utxyBo2TKqdGpzU9RzkzEjd9Tupump": "HALLOWEEN", - "2RBko3xoz56aH69isQMUpzZd9NYHahhwC23A5F3Spkin": "PKIN", - "9Eufcq8yqukb4A9eUTAXrRpzB7aKTdAuUnqe75ttpump": "PUMPKIN", - "7TffjPhjJHMoqdtoA4CWoRyNxwJsYJe1ndGURAVgpump": "PUMPLESS", - "FcUdd49bfuajzmVKxGDkyFWqZp7vXkh1fpsCmtYApump": "PUMPLIQUID", - "DhaLZtprNqwpUYqhrTrtnCGSuxsdnk489UqSrZj3pump": "PMV", - "PumPRGmZ56t3Vngxo6fCP7ZJQ14oUg3biKxXrEyQBSf": "PUMPR", - "AU1dQQhLho6M5YuyRvhvofPrQWt3sRiB9FmsXtW3pump": "PGC", - "NV2RYH954cTJ3ckFUpvfqaQXU4ARqqDH3562nFSpump": "PUNCH", - "Aaku3Vy5m3eBXXcogGQzvhKNJzWEGMcfosQUVyiN82LF": "PUNCH", - "GnYufMbTAMz1DzkSN2DmwkBzjMTLkM22WvQuN1VCbonk": "PUNCHY", - "WskzsKqEW3ZsmrhPAevfVZb6PuuLzWov9mJWZsfDePC": "PUNDU", - "EG1Y8goUGa7y4iRYRgLgwBmSHc4Lxc91qL35cdDNBiRc": "PUNK3493", - "8UKuetxWnpC9Po9Mpf3ptT2wEvkqg9GxZWnnx4VPhpeD": "PVT", - "AyPNhxMEh5n44T8GhFCCzQzUXh2tTRagpQduVYeEpump": "PUP", - "8shTEDxbwvM2C9ELXNHsh8pmkoWovzJ3xgazQFWFapMx": "$PUPPA", - "PUPS8ZgJ5po4UmNDfqtDMCPP6M1KP3EEzG9Zufcwzrg": "PUPS", - "7EW5dDD6MYJK4PcZ89MGApJQwWeDEeNgH4NCVU4qpump": "PURCH", - "AScrhkW2GohYy57rek2YVRc4J9UyXwmbAbEEKkoFpump": "PUB", - "HfMbPyDdZH6QMaDDUokjYCkHxzjoGBMpgaUvpLWGbF5p": "PBTC", - "HBoNJ5v8g71s2boRivrHnfSB5MVPLDHHyVjruPfhGkvL": "PURPE", - "EmofuHSiLiuDasHV4eDKKP9LeoZkG7HmXYdn2FeqKHeC": "POSHI", - "BBG3vpXVCm2uPBD7LUr7yfP9XUXVNJRHMtiMG7q4pump": "PURK", - "3BWH8cDy6djTDhf6FZrMkXBxdbMuidV9wmNW5zPQbonk": "PURRCY", - "28gBfdqoTdbVJnmqZK4hg9rZ7RGJk4Ru37VrbA15pump": "PUSH", - "F5RyYFSXMuTXPjcLBTDr9ihy7ZUw93f1b3gLn1U1pump": "PUSH", - "HptE1m1U5DanCEhhttTmPpG32KepPoaC4AfdkBiC6UZG": "PUSSY", - "FWAr6oWa6CHg6WUcXu8CqkmsdbhtEqL8t31QTonppump": "PVS", - "CniPCE4b3s8gSUPhUiyMjXnytrEqUrMfSsnbBjLCpump": "PWEASE", - "6GjTpbuTx5AWAATbUhE5QHC295fj2gzjYFfNtqSBpump": "PWOG", - "CtqwBpgVcY9kwBi6YvtoQFpheAKXWoq1f9WKPodUpump": "PYLOT", - "FW5GjN29j5ZZCXe1on4HFiS1xvQaChRdfgbfQ9iF12Ua": "PYRATE", - "14qW8bcMkmHQtgJ8VohkR4FXiuYWqvLy4AtpGbuDpyro": "PYRO", - "CreiuhfwdWCN5mJbMJtA9bBpYQrQF2tCBuZwSPWfpump": "PYTHIA", - "HZ1JovNiVvGrGNiiYvEozEVgZ58xaU3RKwX8eACQBCt3": "PYTH", - "57vRMpAeePeuEiEPVtwZv4uXYTLkHeWa9Q5KoGorpump": "Q", - "ERKbvKU1Md4AXNyzWQbagRJWpGE7rwUxGep9ESaxpump": "QAI", - "Aeu42GYcQZyzLiBjixGY9BDbyUyhHPsXrYrUZNcaYdBi": "QEEA", - "mtQ5jWgCqrgBiSut29b4HV19RkMBGA6vidwTMqhNmyy": "QKA", - "EMg2QkFZ6pLvgckVGj8X6nmyrkiuoamYnTdoxXN8pump": "QPAY", - "96hj47MzSpZgVcT1v4nwpbQNEU59f6ys48PArGivpump": "QSTAY", - "4dqYzXnTfNrbwMdJ6uum2FbPC1LkmHtSAbXmh2pfpump": "QUAIN", - "9WfEyNqngVzbvdcpWD7e7TFhgv7T5afBRVHcMw3jmoon": "QUAKK", - "ExnNnP5mpv4MwMUM8QrYbG5PMyGSq7GcU9SrhVnabonk": "$QUAKS", - "hrmX7MV5hifoaBVjnrdpz698yABxrbBNAcWtWo9ondo": "QCOMON", - "2qHqHd1YXSa77DP7XqWvuUPVJbWMmZfbwr1QFtH1pump": "QLC", - "qntmuw54sn81ZawbbA7FwHZZQygBnfXUyHJeCCix5ag": "$QNTM", - "BbtrrZ2ExfCTzrndFBCx5iNDFXxuVxNMdEZ14BZxpump": "QGG", - "F3hQ4u4AsNMSXkhWSRFcPkVbEZwGoSJurviLdbcxpump": "QUANT", - "quantoL84tL1HvygKcz3TJtWRU6dFPW8imMzCa4qxGW": "QTO", - "5woMJykwmESL5N2sR9vYyAjLp1NJXZFxwJ7ejF1nQntr": "QNTR", - "GovbzFQAsXk8rGy5SL54u5Tw6rGaUGnqEypSUDfwswrm": "QUANTAGENT", - "G2ygAeUVpYUhpNsmrpvd2BHTWazyDV72kRpsQSEcbonk": "QX", - "qbioCGDnUBGX5qcK1Fc4zg19GaQEPmxHFMPMZQm4LZ8": "QBIO", - "DPdNMhjBnftREwT82KXytQBk7zd5RBkRrG41L795pump": "QE", - "E4YowrHx5wm4RtSjfuvTqtNH3Wf7NEj5tYZGD9Bondo": "QUBTON", - "2BAKjB47KpQD64m3nWGWrNjC2ZTwWpumYakJVgavdXQa": "$QBS", - "7F9bK2Gj7X5YY3dYfojoWVQYerFYcaGCni6Czt8Xpump": "QOAT", - "AvMdYR4dVLatpMa3YecWhDrerXp5Wx7sNLNTyiA3pump": "QTX", - "E1XGEP1nk3BLxpnkWfqVnpVTA13RYzpY6Na1XD2Kpump": "QUASAR", - "QB1TPvpNUd1GgYeEkdFDDxHx9rjKRfM3VWRzYtkKTwu": "$QBIT", - "8MGGDLtSKi9R9ZoBEDWsrhXetpdWrykRucuVMw7mpump": "QUBIT", - "3MyaQBG7y3SHLQZa282Jh2xtB2TZKHGzNp1CuZ4Cpump": "QUDE", - "HjDYQUb4VfaRkYa34Mya6uNoZ6XdCu16qQGz6RVZpump": "QUEEN", - "BeqAgjEajRgfh1FXggjX3p9iWhNsnrhKR5VjLj1Zmoon": "QKITTY", - "FWaRpuDUhNbDxKgacKBht4mP85LVaohty6V2WD1XShrX": "QSHX", - "4v7TVrzCQPp2nNHn9twrf3eyHQqcy7ieVSLY4xE2hqV": "QC", - "2xaPstY4XqJ2gUA1mpph3XmvmPZGuTuJ658AeqX3gJ6F": "QUP", - "AUuCEHQ7sm2i5GmaHrpE961voWcTY8U6mgrkhcV7pump": "QST", - "5xcRDXCC7CGSMyiaiL127GsLGt6vU3ChK1KWFtRhpump": "QWERY", - "qJVmZgRBaK2Cycm1KqVKnoQeh9bVBNJo4uXq49Htime": "QWQIAO", - "9eEwQFkS1tSTCHqTHdPHa6R9broq1NKdyubReMP7pump": "RABU", - "Dc2XfjTGQQSL1xF9m7hCoUF7nVAGvfG4rZDQPdU7pump": "RACO", - "CzFvsLdUazabdiu9TYXujj4EY495fG7VgJJ3vQs6bonk": "RADR", - "FVFZxEewzrXooU8n8PjGPWKHHzouDjPfW6rzVaeXqGvY": "$RADX", - "CEv97t1Qp7FB6XX1PH5ohyrzUCeKAQbCpAjxegJq9HW7": "RAFF", - "Ak1ZjuCCQbNEjDhe9xwELA9iDX6VPhftcVSzRbibpump": "RAGEBAIT", - "fZD6VmBpiAmgjUbgwRkxzyuXBy7x6XE5d3reX4CeWjC": "RAGE", - "GvV7sFu6FHJsSVXfpG7xqFnWar3c7YkcC74rqe7Bpump": "RAGEGUY", - "G3FoXHoQDuGkEG8ZqQd7riC9uB1N51bg7JuxJEPNpump": "RAGE", - "CmgJ1PobhUqB7MEa8qDkiG2TUpMTskWj8d9JeZWSpump": "FISH", - "3iC63FgnB7EhcPaiSaC51UkVweeBDkqu17SaRyy2pump": "RAIN", - "7ES2bLatqiXAb1bKUGN24av6dSPgRNdWUwPe3SgEBAGS": "COLOURS", - "j1MueUJvnJwRCKzgEQdJjSibhV5oy1uoLZDNsZ3time": "RAJ", - "GQJPirLnGWTgJc4MtFTDiQ5G4y2Q8MjZW9fCpREyF41y": "RAJU", - "G5grLASvz1xzgdh1c1GKy2q55mfuDDXhMQoXnDxWpump": "RAM", - "B3zWuNcVQK9YVHngeDH7UFwsmudWnNhFn89T1AQcpump": "RANDOM", - "RNGRtJMbCveqCp7AC6U95KmrdKecFckaJZiWbPGmeta": "RNGR", - "F2uvr8qmQNMKsLS9KPrXcyqYVXeVS4Gu5iRyS2p2Bixw": "RCN", - "Fw5jbh8eShWrWhft17HqN1osB7NG2rbi6D5s2Zhjbonk": "RANKFI", - "GWaD6wcxSZFwXApoY56x6Cxiqs5sAvtQNdcUvz7Zbonk": "RARECOIN", - "DD8SzmbfyWKTMzHvJb5pGGx8dWxNkiCwZ1r9ZtjuBAGS": "CUBE", - "CTNzPwk2cxh6dFUoysokKU6umNwmJobYx7SYgwJZpump": "REESCOIN", - "D9aVAWMB1wpRbg1t8zeupprqcnrxcBwvnXtcodafpump": "REM", - "B1oEzGes1QxVZoxR3abiwAyL4jcPRF2s2ok5Yerrpump": "RASMR", - "3vJenGaGsuKG5shPhi7rjnuy3MV6xjmBTGjYqYzXpump": "RAT", - "ratioMVg27rSZbSvBopUvsdrGUzeALUfFma61mpxc8J": "RATIO", - "FFXsbx4rPwM8CyKKZiPU3YS9xFFirww6QEzb6KCCpump": "MILTON", - "6hsQFV7DsSyyVmu6sm4UkvYbpHTJhiUyB8fr4DdhSmkg": "RATWIF", - "DEJiPKx5GActUtB6qUssreUxkhXtL4hTQAAJZ7Ccw8se": "XD", - "4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R": "RAY", - "C8wvVNuRPm237bQqqcfRxas77GTK3RzzoBCkWgrGpump": "READ", - "FQt7749yicQ9vZ2CA8r4ghq4MCPJpsVicT8gNkAjpump": "SKILL", - "HKJHsYJHMVK5VRyHHk5GhvzY9tBAAtPvDkZfDH6RLDTd": "READY", - "Cy4DSbZW4CE6cG6HDqQFhXxpHTdm41SY9hBB1JG6pump": "REAL", - "Cy8hMjvArwXKjM7K7oe8peoay2cWjWkW82J1gVjQpump": "REAL", - "8Aq4fWMgPqJbF3w8w8PnLDRVUAFe4mzsCvPKxAArbonk": "REAL", - "9HBLhDZc2hyuDL7avTo14FEVcjs4fQ6yA5JuYL9cbonk": "CHINAWINS", - "3SkjMkrpkqngCCMd3sVU2EuKMVRLTfHEykbzTyrVwBLV": "RECC", - "5Wd2ALxQfnpgQKCyH4WL9giBiiuuLuJs84CJxfQccvmN": "SPEED", - "HELn8rSM1rp8vAjNH4NYXzX6FvCbwWMGqLfaMgiBnZFV": "RIO", - "EBGaJP7srpUUN8eRdta1MsojrNtweuHYsdP3P1TRpump": "REALIS", - "6WVA2BBmNhbHJHcK6JcYczUc2L6omgAhDk6yJhuopump": "REALIS", - "6zYgzrT7X2wi9a9NeMtUvUWLLmf2a8vBsbYkocYdB9wa": "MXNE", - "5UofhCbmro2tGB7Ji49SfqFag8j7ymrRyCmBTCZXGQQa": "SMURFCAT", - "EArkn8uVf8YLfpF2eCdkCvDaPYpQuJzKXxaCnyxXc2P7": "SMURF", - "G8aVC4nk5oPWzTHp4PDm3kAuixCebv9WRQMD93h9pump": "RWA", - "EWQqHFoS7FrDp8Fmj4sN5qV5YKpV3cReCFjiFGtXpump": "RWW", - "AD27ov5fVU2XzwsbvnFvb1JpCBaCB5dRXrczV9CqSVGb": "REAL", - "8mgM8w9BDeZvL1pm86xPDvPw9XHL3vwXkFBqU693pump": "REBIRTH", - "97jof1XfY7h91P3uVfYLocXDVHQseQzoL26n57Tzpump": "REBO", - "DF4zASfiDBr3uuAz4s2Sxtsjo7tDFZiQ2nLeKJGfBAGS": "$RECAP", - "A8Z4uv53e6PS6CYNVNd7HceRCZMHecqpuyzuMWp9pump": "RECON", - "7nZuYZYZnof9gF3zr9QhdnxpQ1mTM8LN3VaJuhrGbonk": "RCON", - "HqKu2gbkHyG1o1rs7d7FYkoES7g3wgfsar5n8qY2pump": "RECON", - "Bmivfdqqbua26hHKJsmBuq89Rhk21Yr7CzpiJYwCpump": "GLYPH", - "GoPffEZ5yuWNnBuUCmwx9idbH9D63xATkrGa6E9kpump": "████", - "3QGGLYLu251jE9Ra8jkGtuHb773xYciXeqqvUgNTGimE": "███", - "HXFrTf9v9NdjGUTnx4sojR3Cf92hoBsQFUxKTN7ondo": "RDDTON", - "DCgZjb2LN5r9Ck5VZAATh8x3sTUHyTzEguThGqh1pump": "ARC", - "B4f5Tj8h8xSoF9zGHxhL1fLQwvgKWcEFhbTMyVhXJ4ch": "ROP", - "C1YT4fbjCkwqodUVLryYVRs37vmvYnihAJjVDDeupump": "$R3D", - "Acud16jqA9j1Hc83JrHYeiN2BpK8LYYQ1h6FGnYrpump": "KOVU", - "7zfnQC53eGtn2H6AxW16sGCmKnnUC8ajCwBVZwd7wtY3": "RED", - "FXXYCLUw6iGDgyRts3HwzcL6MXDeZLfid5EiYqZApump": "REDUARY", - "E6KSaqjvqe2HiUpbEweRxLK4RimQddigm95H9Jaondo": "RDWON", - "4ABXJEK62bfKqPiCbSsUtmb4nfkCNPDGtvLhwQcAWNjc": "REEE", - "uKV2rALePzYtrTayv7nDNEfw3SxAa2boDPcJzwo6REE": "REE", - "BsAEhPN1DKfpjMoSZ5UnDTL4impSYvT9ZpFjnBD2pump": "AIKA", - "8gHPxqgHj6JQ2sQtMSghQYVN5qRP8wm5T6HNejuwpump": "RYS", - "E86mX2yb3HLbJM6gRtZQ6dCYmLh6MSDZadu9SCPondo": "REGNON", - "6HgJHzGpq3fSLmkepsaC8F3VtpUWfXcG4hmUaf4Vpump": "REGENT", - "DP4omjjY94NRJrECHBZyUQSpGrjtukoDyUbqb9Zzpump": "REGRET", - "vQoYWru2pbUdcVkUrRH74ktQDJgVjRcDvsoDbUzM5n9": "REKT", - "HtabxQjpBHTKVoiKBhbUeCJevYjnnEkTyTuqcRQnpump": "REKTOBER", - "5AgHPy5UojpGq6TdtQCNobCZ2tkYmZz4d5p1cqr6pump": "REL", - "CTkPYPMrehsREdzn2dBMCwNxbP9ExrEnoQHMgswYpump": "RELO", - "9XgfFWPxPU6hyDyGtfhC9D6eyRE3RUSgAYKHRznWpump": "KITKAT", - "8wZvGcGePvWEa8tKQUYctMXFSkqS39scozVU9xBVrUjY": "REMILIA", - "remiG7sGaHWgrY7o6SXJW5CYi5A7kmKutyJz6x6hUsp": "REMILIO", - "HALHrqgt8WX3Zi6PjJEBBDiJWMbPxfGEBkjavoCQpump": "RMT", - "rndrizKT3MK1iimdxRdWabcF7Zg7AR5T4nud4EkHBof": "RENDER", - "5MxQUFdPisppdVfjitL6hs492GyikCFnsBWYtuAqpump": "RENTA", - "8x8YipfqZctyTadL2sETH8YbMtinZAXZi6CYFebfpump": "RENTCOIN", - "7hB4AgnZp7RiThvtFTGMedkrU3G8X34fwHzy1vQjpump": "RENT", - "3DK98MXPz8TRuim7rfQnebSLpA7VSoc79Bgiee1m4Zw5": "REZ", - "ezjtoE6PYVnWkbJJYEsGHgR3rXgBydi9eeB1YHP8cVg": "EZJTO", - "ezSoL6fY1PVdJcJsUpe5CM3xkfmy3zoVCABybm5WtiC": "EZSOL", - "M7tBg3eVTm7L1nMgoQsREF2KjBaWW6pmv2C6MUUU5i1": "REPLY", - "7VuQeZKwthC6LiHb8U8fFNibnasiWKSaP9KQmdNLpump": "REPOALYZE", - "6RVA2CGzsonW94GedAqy9Y15teSZADw72wA1JE6wpump": "REPOLITE", - "DUp2qMMGuACziKeyZRtH9cuKyqtYpqJ24iZg6tVLpump": "RDOG", - "EAefyXw6E8sny1cX3LTH6RSvtzH6E5EFy1XsE2AiH1f3": "RPC", - "8CZuGZ2eneMy513c8759sHWh85NjMRk9UjUR9j8tHL61": "DAM", - "Ejqkht2dyN1BaaEtK92zBKY6S8HbVH8APB5sDK9Rmokt": "RUSD", - "85y3AuomcSaUv8tBkxDeoB2GV7HhJrkvxr37PDu8pump": "RESET", - "bzSoL4Ehh4AsaPdVD2ndkeHy9nmKnP8YRwVt3TfaJQq": "BZSOL", - "7G5DM7Jy7TMWKgH313tA3vF6AqHpbHP4TWZzpTVLWv9c": "RTR", - "FDXJWqqhdrKh6TmpVKGVmHk9qyLrizzVfYd6SB7drug": "TROL", - "He2EL8hzZKZEQPZw3TvHCRoBeuh4rQzCBxvv3uCCpump": "RETARD", - "FtTSDNLD5mMLn3anqEQpy44cRdrtAJRrLX2MKXxfpump": "RETARD", - "RAPRz9fd87y9qcBGj1VVqUbbUM6DaBggSDA58zc3N2b": "RAPR", - "EwcVckR7G28Hzzg4XxBnEC3uEYjgKx9kyLQBsbLHpump": "CHICKEN", - "3Ksxijyb1vgCE6hvxqGejdRrQnSTKMXKDSMKki8Apump": "REFI", - "C3DwDjT17gDvvCYC2nsdGHxDHVmQRdhKfpAdqQ29pump": "RFC", - "7RrLheV7dSecVka3MfjYb4Wa6Z6uegNyzhpFeERsfFZP": "RETARDIA", - "6ogzHhzdrQr9Pgv6hZ2MNze7UrzBMAFyBBWUYp1Fhitx": "RETARDIO", - "3Y7uP2UhEE68dpyAmWfXc7biSQmQqQonCiyDp6KYpump": "RETARDMAXX", - "3Rt9t4vM1KShncfMgya69ahrktmKewnHz11FUhdapump": "RETERD", - "HXkbUADfocGyz2WrzJpjEfry8qyNDm5Kwiiq3Mz3tTi1": "$RETIRE", - "Bb43TPQQh5TaoCtFLGjfwAhqcLumN6mTUmCy2ojjpump": "RETRO", - "FmJBuuQ5nhsbAZ5mA41fQn7AtAQvTMFtMMYc5mjBpump": "ROOTS", - "1ugKvQBmxESGM8RX3RmBNVEwwko8i4jRaueYz9kcyai": "REVAUNITS", - "9VxExA1iRPbuLLdSJ2rB3nyBxsyLReT4aqzZBMaBaY1p": "REVS", - "2eu1K3wvfPC7gVj1CK8ohv4ggusdN6qxyxpjHyTCkjZT": "REWD", - "CNKEXXypBC66cZ111Mg3JUxyczXS1E9T6MWEufzQZVMo": "REX", - "J7cENeeQs1XUgfruGNTEjXAAa54Rut8VYdDuMmmBvirt": "REXBT", - "AcV2T3mwLUqMiiqcsafVm35zwPQkmLrfRtaW3716Fzvi": "REXHAT", - "3uxMtF7dT2VSvRFkrLf9Gqm4nDDy2J5RBEXCQpjubonk": "RT", - "Gh8yeA9vH5Fun7J6esFH3mV65cQTBpxk9Z5XpzU7pump": "RHUN", - "65nTNuJGHme4PQvKQyJykKp1bJAkK4A8Q66sd2yBWugf": "RBT", - "Hrh6QCmnGjtq3gqg4xZzncXS7wAWeVmu7SCRfQ4N5H6S": "$RICH", - "E4WZzxRB6hc1Pd6qZAq6QnYemfFT3iwUydqPjzVaPJqE": "RMC", - "9HGziACBczM3VorD3B6ZYtj6BA595jq8erzsSVtUNo6N": "QUACK", - "9ncLaQhTpKCyq542mH5fWeW6cL5BNmiCsToCcJyVpump": "RICK", - "JBahfY5TSFaBooJ5N186Zd9JNvVgm9iHRJSUFT5KqNxA": "RICO", - "GJtJuWD9qYcCkrwMBmtY1tpapV1sKfB2zUv9Q4aqpump": "$RIF", - "jUpa2aDCzvdR9EF4fqDXmuyMUkonPTohphABLmRkRFj": "RIFT", - "AAeENcfHbTExuTvs4q7r9Bjax98Dg6BGX3aMph4bTLdK": "RIF", - "HjBMk5rABYdAvukYRvrScBnP9KnN9nLdKSbN2QPppump": "RIFTS", - "H4kzQgCg24JyDNBVEgQy3VZXUorBuXkwQuW5bx4ypump": "RIGBY", - "dwEPNKQab3iwRmjGvZPXhAmws1W5NsQGwuXwi8oondo": "RGTION", - "6wUfdjiBtXjiWTfwGabBqybVTCAFoS9iD3X6t9v1pump": "RINA", - "3diwvdrQGJ7zaCeCrxQenK9iphmxdKgrFhaW8FtLVBGA": "$RING", - "i6f3DvZBuLpnGSqS8x6WPeStJ7jNe5KewD6afD5ondo": "RIOTON", - "Xs31mE5EiqjSHEaiX9QDKCN6NvSGCqpJ6f1FNq2wri5": "RIOTX", - "FJLz7hP4EXVMVnRBtP77V4k55t2BfXuajKQp1gcwpump": "XRPB-SOL", - "F6eLNTprbtUmo2hLk7GCPmawJapADSfyY3xWJ421BAGS": "RISK", - "93bVs9o8Nq88zxnLWwUAVfN5PXBhCcnV5sfs6AFLno8q": "RIZ", - "AXRsYFt7TXNQ3DcY6BkvRgPV6VsYMURyDtaeudjondo": "RIVNON", - "2uWZLXzbbtmPbJVZjMpm17CPnfL73RFrfvHntsKYpump": "RIZE", - "F2WVwKFRPqE3gMKw22swL5DHgkbdwgEfvhKCoicygcWB": "RIZZ", - "mMKSMNrRT764ZWNgfZyFXxHe5WuTa6ee3peD7sbRiZz": "RIZZ", - "9isJBF9KGWNGga6D4Hbz6H2Gj99ra9JYNxD6Csxppump": "RIZZ", - "85cQsFgbi8mBZxiPppbpPXuV7j1hA8tBwhjF4gKW6mHg": "RIZZMAS", - "8dktTBKseLSsLPpfBYhoKzaAGTAwKqsp6W6UNFtxpump": "RIZZMAS", - "F6zgVJ1Kj7DBa8RyCsPPsgpXJ35z6MDG88cwqA1Wpump": "RIZZMASEVE", - "2qpqweHfSKUvZFGQL3D7aadD5i5dDN8kz9xdZGraGAxC": "RZTO", - "6Fb84TUdMNAVgwRinLeTgLov8dJnk5yhNt41Xq2a6s4c": "RIZZ", - "FNu1NKrgyZQjhaULhFH5phXqUwCPBAMkzbddjdBHfRAV": "RIZZYEAR", - "F4YXmo8CDLRvWGnXyxVZ62wwc7oWcBam7tu4G5kGSaKZ": "RKEY", - "7i5XE77hnx1a6hjWgSuYwmqdmLoDJNTU1rYA6Gqx7QiE": "RNA", - "2fUFhZyd47Mapv9wcfXh5gnQwFXtqcYu9xAN4THBpump": "RNT", - "1HE8MZKhpbJiNvjJTrXdV395qEmPEqJme6P5DLBboop": "RDMP", - "5tB5D6DGJMxxHYmNkfJNG237x6pZGEwTzGpUUh62yQJ7": "ROA", - "RoamA1USA8xjvpTJZ6RvvxyDRzNh6GCA1zVGKSiMVkn": "ROAM", - "8SZ4EsABY82RMNm3Ce2RtKFdA9BPfb4NaoMyCQ7Y3cYT": "STONKS", - "EKEWAk7hfnwfR8DBb1cTayPPambqyC7pwNiYkaYQKQHp": "KITTY", - "G6haJithfeUgSghVpu1rDPCq2NFiYcJFHNpHEANbpump": "RM9000", - "BVdXGvmgi6A9oAiwWvBvP76fyTqcCNRJMM7zMN6ondo": "HOODON", - "XsvNBAYkrDRNhA7wPHQfX3ZUXZyZLdnCQDfHZ56bzpg": "HOODX", - "HrevFGePRXFXXqhssyvteG6y9HU5by1MUegJ1ot5pump": "RUG", - "Xss5RAku5EH6UViFdvW7ss9xQjwQLsrs2opPMhb3k43": "RBLXX", - "CTaQCjEs7UHscQmX4f5r3NqyiMFMxAHNgyNxxXhgpump": "ROBOBOOK", - "HEA4baiTauugXqWzSVVA43oaZhQf7zoUg1kuoSfqpump": "ROS", - "pZHpptKLqcx2ykZu4pbNx4kQHafD3hv7EB3K2wgpump": "ROBOT", - "88nWFvwiLeHTh46Qtb5wTduuAi8tfukaXThJndPxpump": "RI", - "EigAfZW1sYAocChRJXV3FzT8JuhjaSYNWW5CkKdpump": "ROBOX", - "5KxnfDmsXVBNkVHYhW4kztV7ZCCCbrkYxBVrqLWF3G7J": "ROCK", - "E9VQY3VnrpVSekFByzRmfeK1kxgM3UiKCoVVbdUondo": "RKLBON", - "XsKSh3QDynp6oms9yHjZXbZo3pKzUBoqUFKPHS2g9Bh": "RKLBX", - "A2orqVJJVhSTr61dUvRWciTduc3TaZMZADJxmK4UWfGJ": "RSLT", - "4icEZCrEYNop2ZaMMCkRHaNzkt6xG9BpijMCQV7mpw6Z": "ROCKY", - "BVG3BJH4ghUPJT9mCi7JbziNwx3dqRTzgo9x5poGpump": "ROCKY", - "GdbyLsNKHKLXTZVEo8QrGKVmrexEeZUYvhpSfuZ9TdkC": "RODAI", - "5CieaQybZ4m2iMofcSxJZxJ9wKnc1WyE7SWTtqsUp1He": "$ROFL", - "8hN217wBBgRkDiyzabLmSuS95pxX5PFcv3HEM5Ha4ZEv": "ROGER", - "BCQjsvdsoqaSKo8iwgmfnzMV5S2rC7XR3i2K7Ep8BAGS": "RAI", - "4mTgtpb3sgo2gr3uWfa31n9WdG2esfrdgY3URdcApump": "RHUB", - "8hCYPHGC73UxC7gqLDMBHQvgVmtQ6fryCq49tJMCP55D": "STAR10", - "HjDov9j4ByNX9MMgD4w1LhU4DYLB5xABYh7SXLuszqU1": "RONNIE", - "Ha4oiA7TJtoE1jPk8m6r5GPYm1ERmT9oKU5CZAGTpump": "ROOKIE", - "8yev7nLen2PFN2uYGhzsUbu243wMa9z4ZrCwuXs6DEQw": "RBTC", - "8PMHT4swUMtBzgHnh5U564N5sjPSiUz2cjEQzFnnP1Fo": "ROPE", - "CtaVq7fp5xwYFenGQypqdK97LJhTD72GgRsY8e4Npump": "ROPIRITO", - "6FDJbSkiySbetXaELZYaL9im8mAyCkMfHB7rtDt9p3qp": "ROSA", - "BVdMM5Fd7MxcTExnokXZzmCtiLvd2Es5zZQqRLNmrzSy": "ROSCLAW", - "878q23iKgdEWa1wyqg8RCgVaj8CRCsFN82qJfHbmpump": "CATGUY", - "6ZMu4PRVpNMxVHdzMDcGLQf4Qv9x73YHpsHrK6mepump": "ROSE", - "9QSjVAg5rDfBZPhvKwZcB63St3r6bqohP3Adurkjpump": "ROSIE", - "EkVYMGehJKAFowTQpGoxiCpDTRNEoGanwvNUYXhqpump": "ROSSCOIN", - "FgQ3sxj54SmVzttkxuK4bdiEnUNi6kF9Hyx3ezKW72hn": "ROVR", - "E3oEF6TZb5DssCqwyXRt31AUEnGqvcHeVcyGvLiqpump": "ROY", - "D7rcV8SPxbv94s3kJETkrfMrWqHFs6qrmtbiu6saaany": "ROYAL", - "7M9KJcPNC65ShLDmJmTNhVFcuY95Y1VMeYngKgt67D1t": "R/SNOOFI", - "12BvLZtzjdssAycxPeBQUjukhmgQpULAvy6SroYdondo": "RTXON", - "Bg9CZr1CmVoCn2uNWwj9f5rLbmfYRYvcVikCRCwawUwR": "KENIDY", - "mDshrPRV32j79trDVEQjdRsfZ1zdzjwh6eQK9e9pump": "RUBY", - "2VBDM27xPCiqWrFab5oREF4UWJVVXpxGCaTrBgb4bQcq": "RUBYCOIN", - "DKqgMMtEiAMXR8iz4tpVXgnmL8Bgzbu4XPyUrZWrpump": "RUCK", - "2rVgh6tcU1Hhk58XwD631E2D6iqLV28prSdpBXaLbonk": "RUDI", - "AMKKkovbUqr5cxmsg22KxTgaRXq6z4f8umrze4JKpump": "RUECAT", - "FAMfdnr3daYqxP7g9iaSa65RNXnzLWDD8c2uPENKpump": "RFLIP", - "6YGmqZ8i3b4TrDDh5mqComviPUKpwGWTyfFNoWfctech": "$RUGPROOF", - "Fh4oSZ6kn4CGgaojS9dP7gLXbG9KF5Dc9rAkgT6SBAGS": "$RUGPROOF", - "2yHXtrPGH6G8H51nCeN5LbohemtAdgu4uWLjyGYKpump": "RUGPROOFV2", - "HhVM9vHUxbAiRZ9chEecxhF6UdkzveaCG1NC1C3spump": "RUGPULL", - "6F9XriABHfWhit6zmMUYAQBSy6XK5VF1cHXuW5LDpRtC": "RUN", - "9fAT1fVVEKMEL6rUeYxSYU8ZjQ5dSiTzAZYTdiPvpump": "RUNE", - "DvWfXV7MeLjKk3Xec7TQNsBHfQkRETBkuxSH3MFGpump": "RBN", - "M1NEtUMtvTcZ5K8Ym6fY4DZLdKtBFeh8qWWpsPZiu5S": "MINE", - "2oGLxYuNBJRcepT1mEV6KnETaLD7Bf6qq3CM6skasBfe": "PUPS", - "b6LNCreRaHRJ6xa7JmVcPqtAW6NMogzyi4MfcQRuniC": "RNC", - "FS4xcBxLJbrrdXE1R6zHvKw8no4zrQn2rRFuczvepump": "EHHH", - "5mjbjHRb327yvcWUc5WPywhCbYi32pqUqxPUCtpipBLV": "RUNNIT", - "DDij7Dp8updt3XSCzeHCaAoDoFTSE5Y27i2EQ9qjMQtr": "RURI", - "XsbELVbLGBkn7xfMfyYuUipKGt1iRUc2B7pYRvFTFu3": "IWMX", - "6vhLsGaLCdcxkdx5R3cftVB7KS9uzK1CgGtN1ZZ6pump": "RUVIEW", - "H2814bJjmr8N6VrNAQkTsBscN3BSvDn4HDgSceTcpump": "DRPAL", - "BC2CWeUZZvZKniqn4MedxFh12jNjSMLke2Hp2puTXutE": "SAAD", - "Saber2gLauYim4Mvftnrasomsv6NvAuncvMEZwcLpD1": "SBR", - "7Y2TPeq3hqw21LRTCi4wBWoivDngCpNNJsN1hzhZpump": "SACHI", - "91act8ejuXSvR1qEahRjziCPyupDMnUqcm5NL3NDpump": "$SACHO", - "Fntdgqhz4tbX1RNKKk47uEBrhnzvJsMYi4DHCu9utJu9": "SAC", - "HS297WZTnktHXk5DDtaWVQ2AKS4HsJ2CfYTLYVRapWpK": "SAD", - "6btQswYZNDs3g9rn76zvDMcsmMJNYrcQQ6g6VcAYwLoq": "SAD", - "26KMQVgDUoB6rEfnJ51yAABWWJND8uMtpnQgsHQ64Udr": "HAMMY", - "CQcjASdDAvTMJsybZTJJ3hvfdwbi4uTqmtd2SNqYpump": "SC", - "ELPrcU7qRV3DUz8AP6siTE7GkR3gkkBvGmgBRiLnC19Y": "SFM", - "Dc29xEisYZXfCV3WHgyhjnwPYS53fZ1n2YPGFEC1pump": "SKG888", - "Gx5dX1pM5aCQn8wtXEmEHSUia3W57Jq7qdu7kKsHvirt": "SAG3", - "CMe1QWsoDWFUi95GMkk7mCTnnjxBxtnJ3jbrEhQTVWL5": "SAGE", - "8x17zMmVjJxqswjX4hNpxVPc7Tr5UabVJF3kv8TKq8Y3": "SAGIT", - "4rWuWZei2iFNHYpnz5wjMeSvimsJcj5EgpSNvNS1pump": "SAID", - "dWd8vyAH9pQMMG1bkQWiGnyx8LjjuTDHsk8qcsCBAGS": "SAILANA", - "4vfcfjAz1tSA6tWe7cP6Lj7fR8y7VactzTKPxof7pump": "SAI", - "B7e7WzU7pZvbMYFdMw8NSQN3XKfHVGDGesBCfjh6pump": "SALAH", - "7D7ukbcnUNYt7Et5vtsDZhAy28MKu9pkHka1Hp9ondo": "CRMON", - "XsczbcQ3zfcgAEt9qHQES8pxKAVG5rujPSHQEXi4kaN": "CRMX", - "GKHgTd6tqvycgG3mqcZraSZDFR32hXhRgo6sZQtudMsC": "ALL", - "BWWWurbodjGbovFetc3FC6oSbqkdoE62E1XqZ7X4pump": "SBAE", - "ENjwcWn1uUphWtvuP8ypjakr1jMHY9vnznXXrnMVbonk": "SALT", - "4BBjpGwLgGmUxtT82YFK9xMhcvyy3zgf3HpxTRip1YoU": "MUNDI", - "FkbWN4dcFQym2PgCELfThghQqLuA2e2jThMJyhZjfG4M": "SBF", - "DK29axm8KpWExKxxBapPvCTERbwmLsLXpw3f7SRKpump": "SAMDINEKO", - "7ifX7okDF3Q5equtyFo5BkwZyeKY1hGcZCmkZQ5upump": "SAME", - "Ac61nmCxyvqTLzFcPXJQjLsJYFXYRVKkyxWN4kiqcDmF": "SAMI1", - "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU": "SAMO", - "53yANribNp1WzRsciY6upAN2VPY85waZEtADTeJhtQGN": "YUKI", - "5dpN5wMH8j8au29Rp91qn4WfNq6t6xJfcjQNcFeDJ8Ct": "SANA", - "GQz5ThKHNcuAvMKA8rCPSdoFUoApk9Fi8qB9m3Gqpump": "SAN", - "CLoUDKc4Ane7HeQcPpE3YHnznRxhMimJ4MyaUqyHFzAu": "CLOUD", - "EJmUVvDqAdfH5zEohkdS4234bi3c6iunqEMobjmondo": "SNDKON", - "61yG5LCoeoqzBdopXPy6BsVPTGdRXht95L68hj5ipump": "SANDY", - "2EgpQxxRy6SSsXG2YDx8fEM1Z8vyDRHmPAEzSowVpump": "SANTA", - "EhzVcKKmGjLk6pD5gLT6ZrTg62bMgPgTSCXXmANnSyQA": "SANTA", - "7S37Wv8v9BLQ7bCBTSmCgpCHEb7ae9ZVV7YGwDSepump": "SANTAHAT", - "HruKkLrLJ7t1kL8E6HBccSpWdUpnhGmi3SVrB9Jjpump": "SANTAHAT", - "F9mWjEo9y7KqreRTb1pUL7UZHQXLfoUJpy431jx9imR3": "SANTAPEPE", - "4KMGg8YFKFewZsNuRrfqgDLuR15iVtPxEPe9GyFzpump": "SSOL", - "J8uZpXLyrmaPawRFdu81byzRSLqnurTvhQ9nd2ZLVQvE": "SANTA", - "8NQ2HcFBw5oY9wQVrPVnZtQvmeH1atWayH72ErLP2Dcc": "SARAH", - "SarosY6Vscao718M4A778z4CGtvcwcGef5M9MEH1LGL": "SAROS", - "BiAQHYo5Y1umMRbuupdoksKuk6pVbQEHs8fJf8UYpump": "$SASHA", - "EtQE3GREPyFBCU3yUXc5nWs3wRtLYuMmtKAFAvXD1yuR": "SASHA", - "9avVfYKaWV56qnoRpe2xyDrBB3ANkHynG38VrZDhwTbE": "$SATFI", - "tSATdGGSLYBVCrm3pXiib8NmzKcB1iUdjRRseNGssxu": "TSAT", - "E8Cn4QEmZSvr55ZeAJwyo2r8kAX79qzdtQdFudDaZJCV": "SATOSHI", - "EWnSZW86xhJ91nvxPjdZD3g1SqhHj7q8eqY6DEUKpump": "SATU", - "GxihB7J6xfiLDq7iMdgj39jKugt69QoHGDzXqFYPk2zQ": "SAUDIPEPE", - "SAVEaeeqeXNKYb4Lyx28DkUms5gyZ76vGa6fCfdzWfK": "SAVE", - "5eGSrcRv3aXLQDfQhBFrLWU7aKvUD5Zra1fn2oSq6dns": "LUCY", - "2HHgCLfKA5Rd1Fn9xAxfYzS3XoR5NnuxKDYz2EmWpump": "SBR", - "8FA3TPf84h9gEZAxDhiLkMN4vvZcdEUZxmGadbo12auh": "SCALES", - "9mNjA6BizTwpvd4DS3o7BjwZ6aPM9DC2jLHS7JFGbonk": "SCAM", - "3XktN2eZiv5g62qSQ3bbTjTefSxrPjde4PyKDtcRpump": "SCAN", - "8sWKTMrh9bWUrvykK4H3jzEzGbEqvJNpS2f7joYKpump": "SCHIZO", - "7x92Nt2kegSizDxoXvU6ApciGyGrtqp9ykyhUWoppump": "SCHIZO", - "m1nd2qK91tpBrmX7yREJBc6D2fJ15kfiU9cdvxsSN6x": "MINDS", - "8ELspwRkgbz122WrdaX6pfHspHnLFU51jAKuP15cpump": "SCHRDNGR", - "4F5VG4QNMMhbZURpqdvjVkKERtD9KSuf7kPEbAd8pump": "MAYBE", - "XsWAnFM77x6YvpdaZoos79R12o4Yj4r7EVkaTWddzhU": "SCHFX", - "DRVM9vP5coW4WHsNv1kajhn55duyuua4cJ9NGkSApump": "HELA", - "GxdTh6udNstGmLLk9ztBb6bkrms7oLbrJp5yzUaVpump": "SCIHUB", - "2MaFszzCmpJqYPjVXsNytmiHfgckWbtBVq4WSoxjVGkf": "SCI", - "4TcqMXdZBjxirf2vtYikWt1ix3YoHJoFAZwrqe39pump": "SCOOP", - "J4fQTRN13MKpXhVE74t99msKJLbrjegjEgLBnzEv2YH1": "SCORPIO", - "GLPdQwGtjcynj3RLraenPeR9v1REnpdyuciPBpVipump": "SCOUT", - "6naWDMGNWwqffJnnXFLBCLaYu1y5U9Rohe5wwJPHvf1p": "SCRAP", - "3GEznP41VaAGv9yRdBiuYdA8zkffNc8b7DzNJgFbH4Kh": "SCRAT", - "7qBKePC5SqZKDRNsbNhqD6Y6S8JW2CM3KoRv3ztDpump": "$MAMA", - "4u4w3Ey8ewYw7pZWyUH6kaGf7pUx97KYLp3tnFcEpump": "SRC", - "Gwf6QBR2aFypdUAfGzRnog58YRyQgeGtmyx6FBFUwLfu": "SCRIPT", - "E2WYCGJJtWBodVLy1NKcN8ve4UAtsJJBU2mdErbXxP8h": "SDOGE", - "5qKDWkBejLtRh1UGFV7e58QEkdn2fRyH5ehVXqUYujNW": "SCOIN", - "EXtprP1wzrNo2bByrU9JyzqEg2hQMSCVJakeHHYondo": "STXON", - "4p564NQcgjNMyvwjeqivB3XGYtxqHCxvAsyvxybMpump": "SHORSEPRO", - "BKQwzUrcibzZsm1Sm9TVgzxA9G7bAYszCaGJcALMpump": "SEALS", - "Fxgdfsy1Z5Mvh53o69s2Ev6TGxtAJ1RQ5RJ5moCpKmQZ": "SI", - "9n7PCZLVMG9sEViRwz2cmaR4eM72R3ekWuBdEzZppump": "SEAMANIA", - "5MVAy3tHAi44mY2tz4ghqs9T6XRZhDwTMjhdNtjtpump": "SBTC", - "GFJbQ7WDQry73iTaGkJcXKjvi1ViFTFmHSENgz92jFPP": "SECURE", - "5Kh2xc7X2qfRig2DZyGkuEXw7jeXUXoei7U6GBjfpump": "SEDUX", - "6xmo7F4DgFWd2q5xqn4vRqnmP9qSe6Ar3bsNM63S4exn": "SEED", - "FYt532fCsCuoHd9aaX5QN7pZLUTiSXwEjhBmZijgBAGS": "SEED", - "Es6h4XKewn9Mr7V44VqQ5cAUPRY6uV5hhD4vnEZhgoQ6": "SWORLD", - "SKRbvo6Gf7GondiT3BbTfuRDPqLWei4j2Qy2NPGZhW3": "SKR", - "HEojcy5TMAYZJkFy4zhSMYxwYKd22vya9y5TPrWLpump": "SELEN", - "9WPTUkh8fKuCnepRWoPYLH3aK9gSjPHFDenBq2X1Czdp": "SELFIE", - "SENDdRQtYMWaQrBroBrJ2Q53fgVuq95CV9UPGEvpCxa": "SEND", - "4dPW2SdbKcTEUiHpQRTWspT78zgS98v5TMhVpGPoPLzq": "SENDOR", - "F8tDt7QRAYicWQPK5LTh8kZR4zKJPdTiEq5dwCcVpump": "SENSUS", - "CXPLyc3EX8WySgEXLbjhuA7vy8EKQokVJYQuJm2jpump": "SENTAI", - "J73VLpgUiWxooiTwbQyae7h5e29kGrC5RqrbicW2pump": "MEMETIC", - "SENBBKVCM7homnf5RX9zqpf1GFe935hnbU4uVzY1Y6M": "SNTR", - "8GrhA85mcgnaMjyXZ8Hdicayu7byXxbKyEd5UjLnpump": "SERALPHA", - "H9PPCeNYmUZVRQgDtBLnhGoSxCTB4CsJgKvyL32gpump": "SPT", - "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt": "SRM", - "G7pTVoSECz5RQWubEnTP7AC83KHUsSyoiqYR1R2ondo": "NOWON", - "8d4D4FGUrbtTDucywowRD4AnizRKCr1YZaYQ74bRpump": "ANIMAL", - "BHvPQ9dFAiZJUSYJFpEmdfMmgz48iLohyxyixBfZjups": "SHACK", - "E514tLEAdmGFkxwnTA2WMxpMTpqmXASqgRRsrGwCpump": "SOS", - "3c3SsHJnfqCs3WnbM2dBJeNFowcVJDWBfpJExumbmoon": "SOH", - "6WEsL1dvUQbTwjtMvKvZZKqv4GwG6b9qfCQSsa4Bpump": "SHADY", - "R6jVVLcdfpZ7tPLHQXZ4uZRFhBcE2L6aQoPgiJuV7j9": "SHAFT", - "7f9USKiLfbksTfNt6yrF2z6bxNDQ4FRKni8rSEd4pump": "SHAKEY", - "8D1nUMJQam54o34Kj2knFhSTaWoehEr4mBc7LfiDdCqq": "$SHARBI", - "E6Eg7Esj5tfSwkbDGdrzhrotqptv7ghJNarLZ9rbHDSG": "SHARE", - "6D7NaB2xsLd7cauWu1wKk6KBsJohJmP2qZH9GEfVi5Ui": "SC", - "6U48jtR53ZK3E1MozLrpwJDTrtj74uuFhMGNzGY18YPu": "SHARKI", - "6CRayr4GnspC1GUDLs693oPk7AoDSXLXSx9kskgupump": "SHARK", - "H538xSX3ZzVWW9gq9d2Pz6m5eumPwDGvGLarKc1Hpump": "SRRL", - "SHARKSYJjqaNyxVfrpnBN9pjgkhwDhatnMyicWPnr1s": "SHARK", - "iLDu2jjp2i3Uqc2Vm7K7GLiUj3hR4Un49MtD7c4ondo": "SBETON", - "XsEoih2x6nZuUjFwzGoba6MFmtzCkzW2c4YAm6baQbq": "SBETX", - "2G4RMDbXu79f5Yff6fBjKgXknuGvrFCr7iYmiTKy3fVc": "SHART", - "GtPhXAYvP3hmPJ1QSkn4Nu8ajgnuh2RXMdZYugHWtime": "SHAWW", - "Efrq2YCpuB6HdnTSbNQRnNoYXW2xLYhV8RB8KJ2RY5Cn": "SHED", - "9hdynudAhhWzuNFAnpz7NjvdKMfh9z8mcZKNYHuAUgJQ": "SWIF", - "DhteZLhKP6xPNcHVtdYvcE5FFcPN5smU7pxJw14Cpump": "SHEERLUCK", - "D3RjWyMW3uoobJPGUY4HHjFeAduCPCvRUDtWzZ1b2EpE": "SHELLRAISER", - "9F6VbPeM8xR9Nkntyjp5nPY52w9YG9331bLuyhe3pump": "AKA", - "836AY1nD2RrtbYYUqoj39JPTzCwcehrq4M5pvFiPnKEH": "SHIBAAI", - "CiKu4eHsVrc1eueVQeHn7qhXTcVu95gSQmBpX4utjL9z": "SHIB", - "BhPXDQio8xtNC6k5Bg5fnUVL9kGN8uvRDNwW8MZBu8DL": "BANA", - "3UYS3XXYC9yWvNyBWFKqqb5w9YQfX65RQzoAKd8jEUCB": "WING", - "J7Dx5yPbPD5e5BmrSvJyi4n4XRUR2kWqWYaxaSWdpump": "BILLY", - "7wz31sC5z979UMtiqrtxsYYQ6bJzpjUTgPsZvXuZhso": "SHIB", - "F6qoefQq4iCBLoNZ34RjEqHjHkD8vtmoRSdw9Nd55J1k": "SHIB", - "EWqZaPfYg4iGBdHMGYSUDyTgrRrqZhjTfm1DoZmTpump": "SHID", - "42ZFYwLMMbC5fNbbH641PgXeaEym5cJrmnAVho5Hpump": "SHIELD", - "8K7EdeRNW5YbmmCKvAUn4qa5j7Ggz4iAAZbhnoHJpump": "SHIFT", - "2NUEQdSpFFGhssmT9LFUn67gAucyajmgscT7zsizpump": "SHIFT", - "4Q7UJpgqrD51Y9DuJjeKuVnEmanJTbqPRGazVsUZpump": "SHILL", - "6cVgJUqo4nmvQpbgrDZwyfd6RwWw5bfnCamS3M9N1fd": "SHILL", - "2xP43MawHfU7pwPUmvkc6AUWg4GX8xPQLTGMkSZfCEJT": "NINJA", - "D8FYTqJGSmJx2cchFDUgzMYEe4VDvUyGWAYCRJ4Xbonk": "ROCK", - "7hhAuM18KxYETuDPLR2q3UHK5KkiQdY1DQNqKGLCpump": "SHIPYARD", - "FuJkw2vgov82x8DXbhPfmH6S6ZSJxvtjj1DQ7TsMpump": "SHIRO", - "5Xf2LX3aVf1EAwZzEp848qox1PRpjALpYuVNar4KK3zG": "FROGDOG", - "wzpeUomX2VNiswbyYMLqYsdN4nNFZSobNs5xuR7shit": "SHIT", - "42PZx7bPF1EMnP9L7vcjihTx7Nryxh81GG9Xs6fdpump": "SHITCOIN", - "CG13NMFvwJcDY5hzkpBDpGeB9HGRsLz21tk8vkA9pump": "SHITCOIN", - "D3kLDyQRt1QcGi2RXchzKoBuZXm2L2MUjrmNueYGpump": "SCREENER", - "4nswj3o1Lo9iWYvvRJxUD8vbCy9ay7QQoXYcncHNbonk": "SPSC", - "AFbJW5rdaGidnF6o8ZqTtkDBpq3fotSBdJN8fGRN3VRS": "SHITZU", - "EBCqxfSMVSJPDGFLcf2yAfQoeLJRD1z8VbEUemZSbonk": "SHIYO", - "7WASM3L3bkzh1yKSTepguojBFAdmK8ucQjy4DDH5JF97": "SHMEEGUS", - "8XVXzmsMMw7ufa8RC21fHcDP6TGti5y3ZidQinnYurqr": "SHOE", - "H2c31USxu35MDkBrGph8pUDUnmzo2e4Rf4hnvL2Upump": "SHOGGOTH", - "55KDFPScqeshiKrRENKvncD238n3k1SZqLd7ne1Upump": "SOK", - "ivdDracs2s7jCP698dJXKSEQdVrNj9hasJL1Uq1ondo": "SHOPON", - "AEDEBKCEkxHvFx69vKD8EmZ7VqmhAqobubfgMt32pump": "MORE", - "7YFs3oLBEqJffu5VjByokYUTJYRdFrpEFtRngji4LyxS": "SHREDN", - "BiGwCbrNetFGJYZMRJiPMEo3vf5XnLp7ibc1CtmuQGW4": "SHREPE", - "9QMwCyTFDRvPZF14hFHUP7fdiewf1a3PQeP4bewYCLJV": "SHRIMP", - "rrNNJPsVnmhgHR33oXSEstjsKx9mfb4Vim4MuDtpump": "SHRIMP", - "H7ECjS1wfWoPCffZR9KVo5cvYbYdnhaBQoTwdXhpump": "SHRIMP", - "xyzR4s6H724bUq6q7MTqWxUnhi8LM5fiKKUq38h8M1P": "SHROOM", - "Em8WF6pg9FmKDy1dXkWhXxpgmfhj5Bes3ZCE8PCCJSHt": "SHRUB", - "GN78Djb7J2xc2ZLZaJLoGoF8FU6x7DnK3kUJFsnDpump": "$SHRUB", - "7d1vpt5eri79nETcL74Punhp3mGkeBgUkMdPWep6pump": "SHY", - "EjWe2SsM67WzK65xQA6WNwmxZjAnHKLzHFzG6boLpump": "SIBERT", - "7c1GCNc23CmoYVGcETxR2UN6F4dfsHh2WmCEBLUTdNrp": "SEC", - "ERpDpYmuifJUKbaB1Dv9hnJ67RM21Jujmxz18JCeDzbr": "K", - "7JcJLMzqaZNrtc3E2YCp7L1txL2kkYHv5soRTsU4pump": "SIDELINED", - "9Xi7yxDt5oxMG7b66nvbSiNvf7EaA4M3RGjzPmcRvgUk": "SIDELINER", - "8ggbc72zhyA57NdToPZ3DikcXxkS8hnJfdfZVtwLpump": "SIDEX", - "5w3wVdJaESaJKyLmStM6Hv9UyUkmZ1b9DLQquAqqpump": "SID", - "5SVG3T9CNQsm2kEwzbRq6hASqh1oGfjqTtLXYUibpump": "SIGMA", - "4N5jkEmddTxtZQzjb4XowDJvHDzcxkByxMAf9Xa4bonk": "SIGMABOY", - "5ad4puH6yDBoeCcrQfwV5s9bxvPnAeWDoYDj3uLyBS8k": "RIZZ", - "D7qqKEr7JFpAd82m9nvJL2psdPmU1oW54g1LHvDUYFAN": "FAN", - "J6Zh9N6AKkFySTpQD3wMbpoetPpwohvuq5YgVWKvpump": "SIGMEW", - "Bzr4aEQEXrk7k8mbZffrQ9VzX6V3PAH4LvWKXkKppump": "SP", - "2QRjZ12c9JDeoP4LRK1RnWYNeaWFXVFUYag2x9iGpump": "SILKY", - "AXMmTn2bGH6RWdos2fUmXsFLhN4LkPjGv9V4BdJdAync": "SILLYCAT", - "7EYnhQoR9YM3N7UoaKRoA44Uy8JeaZV3qyouov87awMs": "SILLY", - "GRFKaABC518SqXMvBpAVYUZtVT3Nj4mYk7E7xU4gA5Rg": "GOO", - "GtDZKAqvMZMnti46ZewMiXCa4oXF4bZxwQPoKzXPFxZn": "NUB", - "4iipXQQRDGSYcMXa3rqpn5qmxNXEiY2XoLixaVEsGqDp": "$SILVER", - "DVguBpgnixDwVcM654YiaLCMNiY2cdUYJXJK3u9Gpump": "SILVER", - "eQoHHi5TygX4sQ28JPtGM1XpVVvczyVs4bpdoSPpump": "SILVERCOIN", - "7C56WnJ94iEP7YeH2iKiYpvsS5zkcpP9rJBBEBoUGdzj": "SLVR", - "FFqwoZ7phjoupWjLeE5yFeLqGi8jkGEFrTz6jnsUpump": "SIMAI", - "EMeugag3yfyvKqNKknGDWAudNALafZjbv9ByzCE8pump": "SIMD", - "6Nf3nFbyQ1p1CVQYtEZXfeM89XyvkFPyF2JosxdriH7S": "SIMMI", - "5NmDofUfjrAydmLGRyXrbiYRDZ2LhvdEG3jLWHUTpump": "SIMON", - "3joMReCCSESngJEpFLoKR2dNcChjSRCDtybQet5uSpse": "CAT", - "6zsNXPkpGHaf8p71wThVTxr5fhtG8yKHw2TrkiKqpump": "SIMP", - "Chsqyzk3S4uaFdajsQNMp2FnJ4rQMzTd3ykvwyycbonk": "SIMPLE", - "u6VCRrFvejYX8cTnc1ptZT8UqVGbPXPmE8kvcGnpump": "SIMSAI", - "G9FwFW4KzzNGzA18BgWXxmiXfhFTEjdKHCt6bGWKpump": "SMZAI", - "p1ZwX2bBUgojd4CQQRC2RL4wof9xxWaXqguTQFsSiNX": "SINX", - "J1rfosSdRqBbpSegRpCtMQDxDoVsYR41zMywoXkNYiKB": "SIS", - "51XMD1jprfRvd1dbPZLkBuef6xMduwjrmSLxyYiqpump": "BEETLE", - "8GobyUWTAhePKa5Pxrn8qivvRXq1pjtBzRNERRtDpkin": "SIX", - "HqLRjru6pD6GFGnQ7TwSSGQRuPhF8UZNey9T4yCsZzuq": "SIZE", - "4Qur8tvJG195EXmhsuPvpa3qmMiAugtbDaoGwVGV6oJD": "SKAI", - "9v6BKHg8WWKBPTGqLFQz87RxyaHHDygx8SnZEbBFmns2": "SKATE", - "8NKJze6oUVRpbpk3TaYHv2se57fv54qMS38bRtnEZqYM": "SKATECAT", - "12MFXb5CGJ65S3mP3s4eMLaosGp6xGnQe3PxQD38nfJp": "SKIBIDI", - "DPaQfq5sFnoqw2Sh9WMmmASFL9LNu6RdtDqwE1tab2tB": "SKBDI", - "7crBW2CjnJGm56UVS2VRQydPSUyAcKkhRbM1F9kDpump": "SKILL", - "3DFnwGLJUfxc4eMxDgQwW6oWaeKBrJ1iwA26YjTfpump": "STKR", - "3F4aTvvQBdtWj672kNYRenMtMG4jwQE63redg2cN4tCm": "$SKOL", - "79EhLJ14QzGoN1DzKzMxG4DcZeQ6H13AXVLvYxuLpump": "SKULL", - "4fVm6TbqF9SUhVxWr2S49UkNTwUgBjQPQG95K9oeKSKY": "SKY0", - "95WzV2CsTRFBjPCBhQRR4TGVp5kXo8f8qZVt7iVwpump": "DRONES", - "EH2tRrNn2TfD2c1vNLMrNaxa4wskzEnzb1Vo5YDRpump": "SLAP", - "8npbXTu4oTNw3xrMGY1aTm4CtB7cMPVuRUCh3Wqj3fSW": "STACKS", - "Bk6oLMgBNGJKFCCELjjydMqQ1igunsS7y1ZpDDzGbonk": "SLAY", - "5mXL4MPYzSgbKe74Ufpw3xgxtzPmch54wSdYUR4dxZSi": "ZZZZ", - "4SngaZ3N9ELjCqh6P3MeA3r4Hi2Dx1MDn2H4ijcDbonk": "SLEEPLESS", - "7BgBvyjrZX1YKz4oh9mjb8ZScatkkwb8DzFx7LoiVkM3": "SLERF", - "9999FVbjHioTcoJpoBiSjpxHW6xEn3witVuXKqBh2RFQ": "SLERF", - "7okHmAi1re834jSp2kAdduML9a1RfZ5fQJfQ878k8y7g": "SLINK", - "TNh4HphGRorrjLFztWq1ekJakVeWPmLnzymGCE2pump": "SLINKY", - "FqvtZ2UFR9we82Ni4LeacC1zyTiQ77usDo31DUokpump": "SLOP", - "A8YFC9X61bz9SCbmLkccitqM7mXqqHKN2hBZx1Y3pump": "SLOP", - "AkdtuaKVDpsZyeZ8LvcVf4G4L3nJ1Jmd7npmF5mpbonk": "SLOPANA", - "HephxhDavKo6joaqQe7ELo4cu7r636f8RXFgg7evBAGS": "SLOPCRAFT", - "EWWDzCwq4UYW3ERTXbdgd6X6sdkKHFMJqRz1ZiFcpump": "FATHA", - "HQ7DaoiUxzC2K1Dr7KXRHccNtXvEYgNvoUextXe8dmBh": "SLOTH", - "C3Tqmrgp7oKE6x5TmZaig5nVV4EPnQcw2BZyVRXboop": "SLUB", - "Dor8frTyjvgrok3zg95PzVHxnJTKyUP7tmL2Pvo8pump": "SLUG", - "EPpeesRbnZSpe6nJaWSQNyhqrwyjGJPh4GPrzBvkpump": "SLURP", - "7jFfNTP8gBEaXjXoZt3ejJfiT1W87Z9e3mkeYNcEpump": "TOOTCOIN", - "9dzSzFvPsKDoY2gdWErsuz2H1o4tbzvgBhrNZ9cvkD2j": "SDEX", - "55PbiW5TQ9QqcyJN4rUcZsigBhAkBwV1YcGfyAjSpump": "CASHTAGS", - "6gdDALVM7FrTnXTXo17PpHnLtUdnaAtokCBzPUZKA2rx": "SP", - "HPePUpxriuXvUYkm9qUn1CaQeN5Z7yNL46XUTytWthvm": "SMRT", - "6tVZVjcppH2BZ9Xj5yFU1Zt34m2rYcyDqqpSeMDZpump": "MIHARU", - "FS66v5XYtJAFo14LiPz5HT93EUMAHmYipCfQhLpU4ss8": "SMOG", - "GiG7Hr61RVm4CSUxJmgiCoySFQtdiwxtqf64MsRppump": "SCF", - "GPqgnQ5xD8oPGT2aN3bZ467EHmDbEt7aRhJuUQGLpump": "SCF", - "9EL3CHVQS3nwUFhyVT7AGbttRsbJ5UE8Qjnw5ZAtkUhr": "GRAF", - "7XdCaKpqLmKE2K7yr9xaeWB1H2CVZ1oGwxB6hmd9pump": "WIZARD", - "E43qU77tnWDwN11o7TtaGMNpxCAqz8RZEZ7PcTCUXSim": "SLO", - "GjCqkMr8uvYibgummgXXcrD7jtxUQ17Ljtr8uo4zRULC": "BUN", - "3aTpfof6xw6GZAxKXFdX4fGn5BBK2MXdPT8rS4WUpump": "$DOD", - "9Ttyez3xiruyj6cqaR495hbBkJU6SUWdV6AmQ9MvbyyS": "SMOLE", - "FhJkSagsyn2mfXXypYJDB9S9CiYXfzMtaB5bbfevpump": "SMOL", - "Zc4uJVrHC2APe9qKwBD11Gg4BbvQr3naM8f7Wiopump": "PEP", - "8D9CZBsMocuJ8bdnpegGE8auHdFQ1Ez5F66wWAFv3WcN": "SNAKE", - "3kM6vNo8WeCd7DY3EZBjPuFQ9h8gi3Bm5T8rFPQq1WBt": "HISS", - "FuvFLtx68uGjhr4GWET8JTskC7s3R7MM4Feg8toyzFvf": "SNAKES", - "5EdKas4QsHaa71QS1X98wiCTgJCCVMjw5GhY5QMKpump": "SSSSS", - "ELnAjNJJRBaG9MnnZqUXLsbBjkVA19szcduNeD5RCqdo": "SNAPCAT", - "4G86CMxGsMdLETrYnavMFKPhQzKTvDBYGMRAdVtr72nu": "$NAP", - "a2cXfonVgQ6cKB4Lm8YZsPry39VZSA562bwmRSiondo": "SNAPON", - "8oCRS5SYaf4t5PGnCeQfpV7rjxGCcGqNDGHmHJBooPhX": "SNAP", - "CraBDNXPbbJtfPKzmCnWt667pueqCEqSuWYaFNm8JSX8": "SNIBBU", - "4oaV94McVveRosSgdZTn3jkMpr1ge7SQZjwBZ7xG6oA7": "$SNIFF", - "D9mFkgnZHnQGRtZKvnJ44yvoLtJmfBZRahXiUKcAzRE4": "SBABE", - "5eEJpwFtBJTn7n9f6ryWmHjkPUQQghHt4YjvtMSjEEwj": "SNORT", - "Gbu7JAKhTVtGyRryg8cYPiKNhonXpUqbrZuCDjfUpump": "SNOWBALL", - "8uYjdP5spMX64Wpy3i6N2W93R3v9rPiL8Dvx5Uhkpump": "SNOWBALLS", - "JmFLCBwoNvcXy6B2VqABg6m784ubkXpaEx3p7S5ondo": "SNOWON", - "EBUVekfZ1fbqgY6dXXSEE1KoX82mu3qgGBqaQJJLpump": "SNOWMAN", - "8vSwZHKP5WJ8ov6awAntB31Kuyh6NYVwi3qmyZsiivRE": "SNP500", - "4juFfEmgZDMQ8vPECCaoxvAZGt7dBs8DMXXeveu9pump": "SOB", - "5oVNBeEEQvYi1cX3ir8Dx5n1P7pdxydbGF2X4TxVusJm": "INF", - "EsXKXmYAHgmTX1QTf7vL6V9XNrVEJz5KGntwMEmrcBLV": "SCRB", - "DghpMkatCiUsofbTmid3M3kAbDTPqDwKiYHnudXeGG52": "EURCV", - "DXi3Uu7TC2tzJYmnFAgDKnU3p8t6qSafPcLgGaQipump": "SINU", - "FbJpd8yhrGGkWVL1Ujf7qFvTw4uD4675k8CYk82LEKvZ": "COMFY", - "9cC36bMZCt9fF53eLBQCago1czw5L9GRHZBL5adWEyAu": "SOFAC", - "mqL8yXQpeSvc7NgrAtLLPtRvUiWyLoG5RWLv16iondo": "SOFION", - "6wYRTqoERmtRrWmsSCsTcLjNavdTMgxjmVhTEuM3S2tW": "SOFTCO", - "GTc5QQbZSf7W3CpvwqFkWXjPwRhYds8R8iJyR3e7pump": "CODED", - "HsnyqiEdMVn9qsJaj4EsE4WmEN6eih6zhK6c4TjBpump": "SOL402", - "B5UsiUYcTD3PcQa8r2uXcVgRmDL8jUYuXPiYjrY7pump": "SOLA", - "8agCopCHWdpj7mHk3JUWrzt8pHAxMiPX5hLVDJh9TXWv": "SOBER", - "2NiJ4Jn3xBFczAqnYFKc6hEKdAmp5xD8BeBVCB4Tpump": "$SLCE", - "5vXq6Pb2n91W243XLSARY7GtUMcomr7TwfKPRY31pump": "ZPAY", - "HTPHknF5rBNCuZ1qK3muJA6Yo1HEhZj722oSESJnPBiw": "SOLALA", - "AVLhahDcDQ4m4vHM4ug63oh7xc8Jtk49Dm5hoe9Sazqr": "SOLAMA", - "CUwif1FiX5b3bwwb2n5Bm35AixvnR8LJjGUVmEwNZNgR": "SOLY", - "B4iCbe1rhMWE5VhH8nEEfgfyn3FFiNmnGahPVqw9ZBpp": "SOLAMB", - "5c74v6Px9RKwdGWCfqLGfEk7UZfE3Y4qJbuYrLbVG63V": "SOLAMI", - "A9fcjKEw9r4e9b7ATbM6Bk3LbX5jGeZt3WxrmfMRnzDw": "SOLIGATOR", - "Ho2FQgg65oM1zpYuEnC8fULpBnWtqRCTrXRP56AeyCci": "SOLANA", - "GbbesPbaYh5uiAZSYNXTc7w9jty1rpg3P9L4JeN4LkKc": "TRX", - "84Y6h6XoaLAD1zxoQ2CDhcZYRpNsSBKsXULCnpjXpump": "SOLCAT", - "2BeGjx5eYHbGqT2kUZ7K3TvsNGBc65xvorcMqL6kgefQ": "SONDA", - "6DSqVXg9WLTWgz6LACqxN757QdHe1sCqkUfojWmxWtok": "CORN", - "DEADsWJZaonaiZPFkrqEEBGf43mzA5uHeHpwgy9dW666": "DEADS", - "CxGSt4aa83QQcxjpP9oi3pc9roJYJCQcH6UEczkxREKT": "REKT", - "J3mTHLJ3GMcz9ycfvUji5HzeuTvmUQjQSdzChsS5pump": "SOLBISCUIT", - "HUBsveNpjo5pWqNkH57QzxjQASdTVXcSK7bVKTSZtcSX": "RASOL", - "GWcAPiF2cBf5de8E1eSERcnVdH411NqT2peG7zDJpump": "SOL人生", - "9vHizhLqqpTEhVduy12qL6goNMa4GDguLhhiiDKRpump": "LUMIO", - "ELm7r7k1ZAZX3up6gus1p2s3mon845rdVP1aJoN9HfEP": "SOL10", - "B9r1YcdKPg6AmTb4RaFJtucjocozVKJ3gpAcRp7PQ6kE": "SMG", - "BVJJWHJuRLQtv7J3Qn5hMxtFYjywZEgvqorS9ZWngem": "LOOP", - "SNS8DJbHc34nKySHVhLGMUUE72ho6igvJaxtq9T3cX3": "SNS", - "6VHL2vMKgrF1YQFSv29Rs1pj9VCRK29bD11NtDqerqHA": "SSHIB", - "H4phNbsqjV5rqk8u6FUACTLB6rNZRTAPGnBb8KXJpump": "SSE", - "FLJYGHpCCcfYUdzhcfHSeSd2peb5SMajNWaCsRnhpump": "STORE", - "4fUuhtxuqpkonfEAChooeLz4SwB3qqbKebSgLZXapump": "USX", - "2EyNf2PCNT7eQ9ag1RDXw3qn3Cy22WdTA9T6HKDebonk": "SSX", - "C2KG3gB395WfE1P8ZhUvoT5ZUXzQEJst4FCgLAYTGACz": "SSB", - "6BQjp7nPMZNkstxdw2UFH1gf76cGmNuR3bTmSbgxpump": "SUMMER", - "HDa3zJc12ahykSsBRvgiWzr6WLEByf36yzKKbVvy4gnF": "SOS", - "De4ULouuU2cAQkhKuYrsrFtJGRRmcSwQD5esmnAUpump": "SOLANA", - "BHDkUqS5cdmjnXeGPWvbsfMoRbqPeDr9gPf1yK8xyLiG": "SOLWARS", - "2RfXjaiepngcBuGgPLtdnH22g68eetpgzCDX44Hnpump": "SOLANIANS", - "xxxxa1sKNGwFtw2kFn8XauW9xq8hBZ5kVtcSesTT9fW": "SLIM", - "6AiuSc3pYM5rpbKPq8JfSQoMoUsfDpzvP5PmMSEKpump": "SOLANO", - "6Qd6mmFR84jzg24S3851TWNGeTTFrfPko76UiHyRMDMT": "SOLARBA", - "EAwP9LGNjTkQ2YeKE6CGKqBYtrJ6APFvRe7KCMmondo": "SEDGON", - "7rhhtc2DTTAsPr9P3ZRqUjYZESRj2B29L6qrEXG4VLkh": "SOLAREUM", - "3RfQPYVJgJbwyB3BzqqypCEWWryxjTfFDAcXQsckpump": "SOLARIS", - "8EZqVPJdEWt754S17nghfa4Y4jQJ7tigBwsGbo1Tpump": "SOLARIS", - "GNJrBUuUnExsQYBAFy22Hw1qSmSJdWEgy8VawAn3uzUb": "SOLAR", - "SAX2cChnuhnKfUDERWVHyd8CoeDNR4NjoxwjuW8uiqa": "SAX", - "SLAYg2S24PSaBc2Bs916E4PPbpiwNz3YmngLG6EKAc3": "SOLAYA", - "LAYER4xPpTCb3QL8S9u41EAhAX7mhBn8Q6xMTwY2Yzc": "LAYER", - "sSo14endRuUbvQaJS3dq36Q829a3A6BEfoeeRGJywEh": "SSOL", - "susdabGDNbhrnCa6ncrYo81u4s9GM8ecK2UwMyZiq4X": "SUSD", - "25p2BoNp6qrJH5As6ek6H7Ei495oSkyZd3tGb97sqFmH": "SOBA", - "2uRFEWRBQLEKpLmF8mohFZGDcFQmrkQEEZmHQvMUBvY7": "SLB", - "BLZEEuZUBVqFhj8adcCFPJvPVCiCyVmh3hkJMrU8KuJA": "BLZE", - "4nFwuKievw5wcpcXtUDdfxWLyXsEdvgkpENzC9M9Y5me": "BOOK", - "8a5NY9MAdY3NjKsgPUYfqoZcmGKdZkotrjWgZM1dpump": "SOLBOX", - "b6tCZBBD21dFxa1R5tULY1BdJkxCserENFyvseWpump": "SOLBOY", - "DLUNTKRQt7CrpqSX1naHUYoBznJ9pvMP65uCeWQgYnRK": "SOLC", - "SCSuPPNUSypLBsV4darsrYNg4ANPgaGhKhsA3GmMyjz": "SCS", - "A2TjztayKEavSCR7deVs6zkKGLP155pQv3j8ByVJ2WLW": "SOLCAT", - "3WKzqdh3ZW3tP2PhAtAuDu4e1XsEzFhk7qnN8mApm3S2": "CAT", - "AMjzRn1TBQwQfNAjHFeBb7uGbbqbJB7FzXAnGgdFPk6K": "SOLCEX", - "947tEoG318GUmyjVYhraNRvWpMX7fpBTDQFBoJvSkSG3": "CHAT", - "CTRLb9PmyRL2fQZ4amRKw6ydJXbTLc5LWvw83RqyjxBk": "SCTRL", - "gr1qPTo3tpMAxt59BftQo2uSfRHRuUZJaWLhR8ADtwz": "DOCS", - "8ymi88q5DtmdNTn2sPRNFkvMkszMHuLJ1e3RVdWjPa3s": "SDOGE", - "3FG9DAv3CDBEh1CQJhRM9BYe5thUMvP5GmW46524Qahv": "DRAGON", - "w131jbryFvFEmtqmZvx42Meiuc4Drmu3nodTdVgkREV": "DRIP", - "GUhVUdAgT2VPaDL9EtCTqBptX8ENrgs5WqR3c8EJpump": "SAI", - "SLNDpmoWTVADgEdndyvWzroNL7zSi1dF9PC3xHGtPwp": "SLND", - "2wUGjvMqXusgfzYP3Vj149bSM9MwTPLS4maxkdGfpump": "SOLENG", - "TuLipcqtGVXP9XR62wM8WWCm6a9vhLs7T1uoWBk6FDs": "TULIP", - "BjBzvw6VX7UJtrC7BaYLG1dHBiwrXP1T9j2YfDEdP4zU": "SFARM", - "948H4FquubpPQxgeYxoo8kiKU12qtRuon8ccnD9r222a": "FILES", - "CrUT8HNd1wi2hPqjTkADC7qUgijwNFDDWR9CW5NsHXvv": "SFLOW", - "BLiUhfaUFbSoMVeRqq3QYTA1eJsTsB3ZDEYtUsGGQ9em": "SFG", - "9HXewqNjkHz22AKds3JUzFBNguzhoH4cJ4h1EksZyPGH": "SOLGOAT", - "3fcZGkXf7xK4pBAxiyK13DXTC9x3cPxHE998YUbGuS85": "HIVE", - "METAmTMXwdb8gYzyCPfXXFmZZw4rUsXX58PNsDg7zjL": "SLC", - "SoLiDMWBct5TurG1LNcocemBK7QmTn4P33GSrRrcd2n": "SOLID", - "BQo1usUXscfXrECeYKp6PxaMfvjns1XxFSwRnu5HK1bu": "SOLIDE", - "HBB9ZWZb8CFLrTB1ZV4xFuvmubwsCPAjZSRzFuVPpump": "SOLIDENT", - "FUW4poh6s7uychceF8u1mo7NS65vzjX5vmS7Yi7GYQnz": "AITECH", - "72Sp7QibTt8vWoHzvJQ6QA1Pp6UuNjCtRDBdioVSpump": "S100", - "XLqNprqfebg1Qkn9ttjHv7rM9g5JkvZoyvo5Lx9pump": "SOLITO", - "2AFzzF7EKDygNJ2VthGWNSftCthpSkTmqSg9p8bFsend": "SOLJAR", - "8RZnGj4hdcai1HB9KMr1ZEUwPpWRPKQDduUxLbnnVnmq": "SLN", - "Eyc4ozMWwUxBUTK61MTjzLBjSWWWNqqc8sjTF3Gfbonk": "SOLLE", - "36CEGUfsUU6XXPHPXi62NKXoQ438qN8o1EZM1dgm6DFP": "SOLLY", - "C8cNX2D1y3jqKpMFkQhP1gGbfvTEdeckZXLBKSN5z5KF": "MAIL", - "FEhfph34VeoCfkuiNnv89pEGPiGPukWfhrKtLko66mvj": "SOLMAX", - "BNT4uhSStq1beFADv3cq4wQAVfWB392PjAaxTBpNeWxu": "MEDIA", - "7okSdfc6YKv8ouWaWdPEyHJcNBuJZw4QwPzrah8NNi8h": "SOLMON", - "NAVsy3R5W6WzhZxKzPc8PZ3r4hmjTd9XBYyfYeUrMNL": "SOLNAV", - "DeaKMzAeZja3Mh5okZE6WUvygLP3Lfuvm6Rg78HqXTz9": "SOLNIC", - "SoLo9oxzLDpcq1dpqAgMwgce5WqkRDtNXK7EPnbmeta": "SOLO", - "B5PucY1LAe9WA6Tsp8ACnqw2ofWbDxyKZxiin11ha1QU": "SOLO", - "Ex5DaKYMCN6QWFA4n67TmMwsH8MJV68RX6YXTmVM532C": "USDV", - "J8cKU4pD2NTSovvV5XghWHQiJy5TTEzgSyozorxz6ax8": "SOLO", - "HJSnJaQv3u4ZyvPXiQPTyBsYJpggWsZvVH8yedjBpump": "LOTTO", - "29xJEmh5sMo2DqapmcK7UardZP4B88qJZCZ2BGwa3mJ7": "SOLPAC", - "GfJ3Vq2eSTYf1hJP6kKLE9RT6u7jF9gNszJhZwo5VPZp": "SOLPAD", - "BDHqX9YfJE3M6caox3obUX5YpWHz2cjnGFiZJtRghdCo": "SOLPAKA", - "4MeqZ8v3XbNVVX6B69XxXaymoTX6DbK8iZgaGngCbonk": "LOLA", - "3VW31dwix6k2EdzhDgZ2zB15J7FbHYQwAUqXgktRcJEX": "SOLPUMP", - "SLRSSpSLUTP7okbCUBYStWCo1vUgyt775faPqz8HUMr": "SLRS", - "SNApmcWQqj3Ny2YFkQmkELQnNgaXRu6KmnYSPiFZcLn": "BLOB", - "EYw27YGciv1KYbt7nSpJJZMYmg2v7vgkWpZbC4Pbpump": "SPL", - "3ThdFZQKM6kRyVGLG48kaPg5TRMhYMKY1iCRa9xop1WC": "EUSX", - "54jVZGHyWURX5evBtZqUsJjwoKzcZJbVokDU93AUZf2h": "STREAM", - "HRNDGEd1vDAQwgAU5XftdFFmbRVWSnJBp8rKvAvpump": "SOLTAN", - "jk1T35eWK41MBMM8AWoYVaNbjHEEQzMDetTsfnqpump": "SOL", - "2x8o3hA5S5fBxCSE9hzVTf3RohcMWHqkDNKNEPuzprD5": "STBOT", - "2qT8JVotQ2C1gKbqpuqNatkpSBWxiKHbXkCyTqH9pump": "SOLUNA", - "SoLvHDFVstC74Jr9eNLTDoG4goSUsn1RENmjNtFKZvW": "SOLVBTC", - "SoLvAiHLF7LGEaiTN5KGZt1bNnraoWTi5mjcvRoDAX4": "XSOLVBTC", - "SoLvzL3ZVjofmNB5LYFrf94QtNhMUSea4DawFhnAau8": "SOLVBTC.JUP", - "7aWQoyfAxXUD5Not1Fc48TJAWs876NUvKh6Vp8WSXcNR": "SOLX", - "7UN8WkBumTUCofVPXCPjNWQ6msQhzrg9tFQRP48Nmw5V": "XENCAT", - "9g5UvB8iR7Fe9VekHXytWTAXTHgY9Gem9g2x3SBCvRs7": "SOLYMPICS", - "31iQsahfa4CMiirU7REygBzuAWg4R4ah7Y4aDu9ZfXJP": "SOLZILLA", - "79bqt5R9YKayWA3iXXeSNSAid6J23a5rddqhmoHypump": "SOMA", - "9NrkmoqwF1rBjsfKZvn7ngCy6zqvb8A6A5RfTvR2pump": "SS", - "B582oxHHy41ijxaGem3GksSWAVBfr2fJxNKrL7mpC1z1": "SBF", - "354jgbb56NmBnyd647sPmj8S1md9cBeiCPPhT6pQbonk": "SOMBRERO", - "Gz7VJWyQW6Uqt1Py5S2ZvtYNEFbP4Thmi31n5mx1pump": "BIG", - "8SCLpKVehUzuFBkqnF1qZu5LrodQDRCEoPVpCsrspump": "DUMB", - "GBXFsey6um16Lwfafo6etBCynU3iEST5e4Y49YMJjupx": "S0X", - "sonarX4VtVkQemriJeLm6CKeW3GDMyiBnnAEMw1MRAE": "SONAR", - "BZ5ZaPicPT4nik6yNV8yhrmL7No4ziLbGKAofEUYTbWG": "SONIC", - "72h2cpj8YU7aUJW3NFMjxfqGTh6hXnUrovMC4gKqyFPM": "SONIC", - "SonicxvLud67EceaEzCLRnMTBqzYUUYNr93DBkBdDES": "SONIC", - "59u8qAD2S2gEtsY5Vs7dJ95YspnHavyHEtRfzCEb9F7G": "GOAT", - "9YYoJ6NaCm1kY76oYNF2uAWTRnJ6nFc4Q5MujeyNo28D": "SONICWIF", - "H32RvrcA2tHipbXzaiEt8Dyr5u7GfnME6RcFDDWgpump": "SPQR", - "4eDf52YYzL6i6gbZ6FXqrLUPXbtP61f1gPSFM66M4XHe": "SOON", - "6NX2MWBuJM2Fn63K4hUgMPivLXHV8pwsU1yTdmjKpump": "PSOQ", - "E9qT2T2YHfD67X7VKXZ7PWGnhaZ1nYdEBGbx9xbKpump": "SORACLE", - "89nnWMkWeF9LSJvAWcN2JFQfeWdDk6diKEckeToEU1hE": "SORA", - "3gh9ofmjD7acmNpMo41UvyRqoQvaH7N3mypHScziUSRY": "SRY", - "49jdQxUkKtuvorvnwWqDzUoYKEjfgroTzHkQqXG9YFMj": "SOSANA", - "2FK2ZY4EYfeUtD7gnh4uNdWf2uQjqBJ5vwn5ja3gpump": "SOTER", - "Agentfk87X48g37uPprEZaQtizDvnSQVeTqVjAtmbhd3": "$SOULS", - "9doRRAik5gvhbEwjbZDbZR6GxXSAfdoomyJR57xKpump": "GRPH", - "FsXJHch6cj9TyZGm1kfMwt8S8oATCEr6ccqWFp3Xpump": "SOULGUY", - "G3ukjeHBrDJ1zUFr6KandnW4bPcjmvK3qL2uATRb3F63": "SBOY", - "FNj16zAMFERd4w2nKfjfVn51aW38m56jcfNHzpVQmoon": "SOS", - "J4ywFdm8H7hjwKzCaEQujhkDRfCnRviVnHMvFNDAoLNQ": "SOUL", - "vE2qArmjto6VfeMngyGAnzp2ipLYeXsxiARDnnXondo": "SOUNON", - "EANjzFjj3nPXHdzN5CE3Z8LLVn69Ce77FE8X4cvondo": "SCCOON", - "aKzjn2ZdWySSGPSSDTY2HUpcSCmemSahTXihrpyondo": "SOON", - "6tWuipcDv4CiHtXid7JctDbtLLA1VdkTwSDupK6UxzJL": "KOREA", - "4G3kNxwaA2UQHDpaQtJWQm1SReXcUD7LkT14v2oEs7rV": "SOY", - "AkwneToXzViQHQekpCtNW9sBn9oEjTwqT4JYwGgSpump": "SOYJAK", - "2uic5Siiu6kwPoHZDHnhvH1MySx1vmLkVmzUTkc3pump": "SOAI", - "AVw2QGVkXJPRPRjLAceXVoLqU5DVtJ53mdgMXp14yGit": "SPYR", - "XsoCS1TfEyfFhfvj8EtZ528L3CaKBDBRqRapnBbDF2W": "SPYX", - "wPGmsXKE3csuioed4362kcQsrxGvhnGXGgA4RhBpump": "$SPACE", - "9fLGtf1rRkYd6Dy93hvYh2r8T8Wy2EPa6WHC5ibgpump": "SPACECAT", - "HovGjrBGTfna4dvg6exkMxXuexB3tUfEZKcut8AWowXj": "FCON", - "PreANxuXjsy2pvisWWMNB6YaJNzr7681wJJr2rHsfTh": "SPACEX", - "8uz7r3yQq8xnwBfWCoQGAftyYKgUz8wMQWod884nerf4": "ESP", - "BMMWMuy1ZFBtDEzzdBvtrNvMZFetcGUfkrdDAYo3pump": "SPANKMAS", - "G8FMb99PyHSvW7uQvdRCMfkeZgcGBwexR6BiaMxfpump": "SPU", - "SPaRKoVUfuj8FSnmbZmwAD1xP1jPEB4Vik8sgVxnJPq": "SPARK", - "pC9Wo6oHLJx2Vwrvrtpj64mRHQPFYwvGSr4eR2apump": "SPAWN", - "hWfiw4mcxT8rnNFkk6fsCQSxoxgZ9yVhB6tyeVcondo": "GLDON", - "k18WJUULWheRkSpSquYGdNNmtuE2Vbw1hpuUi92ondo": "SPYON", - "Bm8MHt9vwK2RapFFZLa4AWfwLCzXAqCxanRj81xWBAGS": "SPECTRA", - "5TRsf8t14xpoBYnzfUQyjk5SExCYqYzapEc8f6fEnU9k": "RUN", - "C7K4Tot6fnnNwhWpqw9H277QPcP56vHAEeXubRHDyCo9": "SPEERO", - "97SWAaoH62g1fYXphiLh3XczSBZeF5gBQBz7spNZpump": "SPERM", - "JrTYw7A9jihX5TwpRStYviEbsYf2X2VJpZ13719ondo": "SPGION", - "XsTuW2zaiPbzgrnQ7b4Z99zeULMM7gPxppE61CtpyDj": "SPGIX", - "SPQkEcsdALLLn4MqCb3XH64hYTmdD4fZZk9CF5LiQV8": "SPICE", - "AT79ReYU9XtHUTF5vM6Q4oa9K8w7918Fp5SU7G1MDMQY": "SPDR", - "BX9yEgW8WkoWV8SvqTMMCynkQWreRTJ9ZS81dRXYnnR9": "SPIKE", - "sPiKEYAqoaGYYBAnPxro8NLSYLu93sr56n352jJRLN5": "SPIKE", - "spinezMPKxkBpf4Q9xET2587fehM3LuKe4xoAoXtSjR": "SPINE", - "VaxZxmFXV8tmsd72hUn22ex6GFzZ5uq9DVJ5wA5pump": "OIIAOIIA", - "9X2jTh2SPKBUBWD5wYuTBs1SsCJXbePYB8yfiVCvpump": "BUSTER", - "8Nd3TZJfxt9yYKiPiPmYp6S5DhLftG3bwSqdW3KJwArb": "SPOODY", - "FdGoS1Dok5CLnS8fVSmj5A92uY1yhzdTC2ZxuLJdkwgs": "SPOOKY", - "8bdhP1UQMevciC9oJ7NrvgDfoW8XPXPfbkkm6vKtMS7N": "SPORE", - "AhpyRU8g4kva6JAcqTcNRBvi565a8g76CpG8ocY8oMoo": "SPOT", - "jzCvs2Pk8tDcfsFRqnEMjurgaQW4iQfEkandUR8ondo": "SPOTON", - "AcfKwAf4UQLA5DLRQD2eQxW6pLv79VKzVso38WMfTxGZ": "WIFI", - "9wyheK9Eqy92cRPM4PBGmtZseizVJ4ZpZgF8Jcswpump": "SPQR", - "FRVCEaJuS8cuBTWR1JwVu35pBsKmHmt9n4LjSiF4pump": "SPRED", - "V8LRV7kWjrx6Prke9oHEHNUiR122BVtyuPciTCTondo": "NIKLON", - "JC6NduxxLvvV7X5Pb9agHQTRpHTecB83HhGK4VZHpump": "SPRT", - "XsyZcb97BzETAqi9BoP2C9D196MiMNBisGMVNje2Thz": "IJRX", - "8HJ81sHVsmJMzm6XBfTgywWQXwQTmVdmXaQHm7htGPH2": "SPURDO", - "CuWmZpqoVgJQuo3YVQqR6zrV3eojQfXuRmuPtQUzpump": "SPURDO", - "J3NKxxXZcnNiMjKw9hYb2K4LUxgwB6t1FtPtQVsv3KFr": "SPX", - "23k6Jiahmd11DWBzE7jpprsYzgs3m3LfNtw2XkwBVfmG": "SPX6969", - "CsZmZ4fz9bBjGRcu3Ram4tmLRMmKS6GPWqz4ZVxsxpNX": "SQR", - "SQUiDbpt993WzEa1WLUEgYJRtKxsQAKjoELH9xhMs3v": "SQUID", - "9bWCY9nEj7bGpaLU12RSEpBUKkuWTYatWakHVepJpump": "SQRL", - "nZuEp76CPtVFy7SsRh9DhTin2uCEPceEnKbLaWcpump": "$SQUISH", - "STBuyENwJ1GP4yNZCjwavn92wYLEY3t5S1kVS5kwyS1": "STB", - "4AjvPXMn8YZG9saAVJvcWspg73oTFf2JmU4in4Xgpump": "STABLE", - "DBAzBUXaLj1qANCseUPZz4sp9F8d2sc78C4vKjhbTGMA": "SBC", - "8mYyeBom52k3V7qcQWkpSPcD6gPHmP4KK9Uf7DJ6zREV": "SC", - "AcUZXCspGisFM9pBWQZg5944uJv2dHyqph979ve51to1": "STABLE", - "4qnVjPG8DxoYYJifS83iExe3GWnM4JK4b6mBZRaR4gs9": "SD", - "7hUdUTkJLwdcmt3jSEeqx4ep91sm1XwBxMDaJae6bD5D": "RSOL", - "8BMzMi2XxZn9afRaMx5Z6fauk9foHXqV5cLTCYWRcVje": "STIK", - "FrMU7uzwoTwad8E8aLBtHbQcKesZHaT2sJkZFwcKoDua": "SFY", - "DUSDt4AeLZHWYmcXnVGYdgAzjtzU5mXUVnTMdnSzAttM": "DUSD", - "5yptSfbPdg16eKeGQXfrPVC5muVkjQWnbNcx7VDVpump": "STANK", - "CQSzJzwW5H1oyWrp6QhfUKYYwyovbSiVDKnAxNfb1tJC": "STAN", - "2dPkLQi1WLkrbQbBAS9d8GHARF4oWQXFXrUbMq7bstar": "STAR", - "ATLASXmbPQxBUYbxPsV97usA3fPQYEqzQBUHgiFCUsXx": "ATLAS", - "poLisWXnNRwC6oBu1vHiuKQzFjGL4XDSu4g9qjz9qVk": "POLIS", - "iPFqjcZQTNMNXA4kbShbMhfAVD8yr8Uq9UtXMV6ondo": "SBUXON", - "A2GHnfpZvyeZX5Pr63jdMmo9uYbpaaKqHHuPD5xD2n6v": "SC", - "FZnSMd1hPu5MVgttmtfDZUPN5gcuexvrh3CB67UuQshb": "HELIA", - "7MiLCuSZfLoTAK7S7CztrLV75kC3rfJULEmDNUx5pump": "STARECAT", - "FsTc9VgMQoHAc5o8DdeAFv1RphgNjb4kZTxx8JQSpump": "STARGATE", - "FAJW358HjJ2mHXSHbHyxghfVGzX5SBoupdjRr2y9pump": "SONNY", - "HsRpHQn6VbyMs5b5j5SV6xQ2VvpvvCCzu19GjytVSCoz": "STRK", - "HCgybxq5Upy8Mccihrp7EsmwwFqYZtrHrsmsKwtGXLgW": "STARS", - "A9v1uzDFSon5X7TgGYqd2HFpcioJntkFfpozzLCdpump": "STARPAY", - "2GZcmRHmKFWPqkJ9Wm1XAf5kLwFxcYG5cTiTGkH4VZht": "STARRI", - "GHtLF7drbYXGTHX73uSxqPKkJUzDqcBNe2M9fzjJzr3j": "STSHIP", - "97PVGU2DzFqsAWaYU17ZBqGvQFmkqtdMywYBNPAfy8vy": "STARTUP", - "2QjmbYxzxD5twLHWDhXWQnxZsRF7igyBfs3VNEdopump": "STARTUP", - "EWMfSJgDCE7CXDAYz3hbCaA7NsFHTnddySXx3shco2Hs": "STASH", - "Dn5cVZ9AvJVzk5b7za9YNkzkCMbrXkPvEbC4wMBMpump": "$STATICKY", - "AbktLHcNzEoZc9qfVgNaQhJbqDTEmLwsARY7JcTndsPg": "MS2", - "3qadpmJCfs6RxKPxpofA3CyEj78RKcjVLyoyVY6xpump": "STEAK", - "BUfMXdTHqKGVztQjYRB2b4v7EKAv4cheMg8Uu16Npump": "STEALTH", - "StepAscQoEioFxxWGnh2sLBDFp9d8rvKz2Yp39iDpyT": "STEP", - "7i5KKsX2weiTkry7jA4ZwSuXGhs5eJBEjY8vVxR4pfRx": "GMT", - "2s8AT36jzHfYDnkT1rTTFZti7EkVHhXon6ptAi8cpump": "$STEVE", - "4TafVse4Sf35tLXQkKhJ8tTrAMCLWVBWvg6CK2xwjups": "$STEVE", - "HrqHpE4NQbSTjVtFSTm9i8omFfytggsS9UkccHWHWntx": "STFX", - "FECuKNNnGAzhF8LJMV5vGZUj9psDfWsu7WCNUBW7MXqo": "STICK", - "Ehxi6CznHg8VnnqTRdRkiLT7cuniYaVS3f4wK5Dhpump": "STICKBUG", - "3fAddCWnFZnVfHRaDf86sVDAD7jHXq97NULrpo4GGkth": "STICK", - "G1jydhghPMLBXRFPR3wyJ9HbpKPR3Rv7M8Yakfo5pump": "STICKMAN", - "9ZWmgaewdaH2Ljfi3iE8A7umcJcxJz83ZakkfEPApump": "STINKCOIN", - "8jVcgXXUReoiFCDzDMEhftCvA7HWqLX6rNmZ6AxLpump": "STOCKCOIN", - "9KczPhWgTxeNMgqe216Q36u3c3HTQ5WihuAmT5AKe3e1": "STK", - "8TzfHZa6ZnvvGsQfnFC5wGrCiqbN9nD2KMjyabfrpump": "STOIC", - "43VWkd99HjqkhFTZbWBpMpRhjG469nWa7x7uEsgSH7We": "STNK", - "6NcdiK8B5KK2DzKvzvCfqi8EHaEqu48fyEzC8Mm9pump": "STONKS", - "27U6sAYSDUJLpeCTTL5gW2wSwLGNRZRZKWJEqTWGbonk": "STONKS", - "8P6WDZ59qwMrTgXWaxFN5J19dGwvY2XEavCyRdA25PNm": "STONKS", - "AHnZ7VyyQ5jHXbitQL8tuN7ciGG66EvCnU7eKoKX99fz": "STOG", - "AxriehR6Xw3adzHopnvMn7GcpRFcD41ddpiTWMg6pump": "JAILSTOOL", - "2tuw17xp9dBxCW72HFvRAhpawMtc5Lcag3GGBktCpump": "SSL", - "CofiHxizezmiQV1r85eV4Midefb6BahAyVeQQQxVpump": "STORAGENT", - "DtwN3PHiKEpK1MSekViVC8QifdRJCkML6HG16sXHpump": "VAL", - "STRXzrUKLEpXTVN5oyXaSZgt38MUcWN8VRAZUAVFX3j": "STRX", - "51Yc9NkkNKMbo31XePni6ZFKMFz4d6H273M8CRhCpump": "SMS", - "GUdXKe5B35QBcqa8VrPQqn8LCQrK2X1aecCQSJs3EVrS": "STORM", - "33dhma9QA7xefhVpA7c5vMrN826xcHanSHruUEKF2LmW": "SOS", - "DpxKNEi3XVeRByaGqYKvz2w6E2PhPgBAqdayLcQEpump": "SOS", - "BpdHpqznEgYPXZNrJVRZvBhdWoafYLVVuLxTQo34pump": "SSR", - "XsaQGz41BEQkS9xAB44uvUtuXcdLJAXEU1dogEzWMZ8": "STRKX", - "Xs78JED6PFZxWc2wCEPspZW9kL3Se5J7L5TChKgsidH": "STRCX", - "B8GKqTDGYc7F6udTHjYeazZ4dFCRkrwK2mBQNS4igqTv": "MSTRR", - "9BpQUsVpGSBXX3fwi3U5Re12pufkiooxvVvoDCitFbJ5": "SVM", - "D2tztvQKcmo7PUof8quQWAu3qXKwtiW1pLjvHLsUakv5": "BERRY", - "2EYvskXTncMS11vejJcWHh8fPaCFy6bzDcVnmofEpump": "STREAM", - "3arUrpH3nzaRJbbpVgY42dcqSq9A5BFgUxKozZ4npump": "STREAMER", - "STREAMribRwybYpMmSYoCsQUdr6MZNXEqHgm7p1gu9M": "STREAM", - "5Qw7bkvj13Atg1ftzUKrQu9p7YrDiQmJTrhwchM1pump": "STREAMGUY", - "FNhcY1cwQvQqaM8CUjXSuoGKJniwC4maBRLqNRLipump": "LOCK", - "986j8mhmidrcbx3wf1XJxsQFvWBMXg7gnDi3mejsr8H8": "STRSZN", - "9eF4iX4BzeKnvJ7gSw5L725jk48zJw2m66NFxHHvpump": "LIVE", - "STrikemJEk2tFVYpg7SMo9nGPrnJ56fHnS1K7PV2fPw": "STRIKE", - "9HCRyuqrFDt1kUrwuUF7xjb2eqCgFmC5433MjqJ3STRX": "STRX", - "strng7mqqc1MBJJV6vMzYbEqnwVGvKKGKedeCvtktWA": "STRONGSOL", - "GWdFcYSbpQLywCe5qQkBPeojHGQHmX3L16SQM9mq9fCt": "STUDY", - "4zUDFduzd6dWisjkrS7d7VSEw3EdrV3t7KkYPznZpump": "STUPIDCOIN", - "9RjwNo6hBPkxayWHCqQD1VjaH8igSizEseNZNbddpump": "STUPID", - "w6iohhdC6zbq2DP1uwtmvXPJibbFroDnni1A222bonk": "STUPID", - "ERYH7V18RFsXhq7XGbojkpamb5dw6Qyto4kd1Ripump": "WLFI", - "HswqmpB6is4ih9pPLNwYCFnG7YbAdwKvdZXW18aKpump": "SS", - "G2pMCBjRQHHCkE79r9KAESvdhUCieWPZvX5GRFa3jCLg": "SUBY", - "9X2RHtKrBzw3SLYe9E88cBd1kz5RfU1f4JTSn4aRH43d": "SKID", - "EA6zg6e9RcqWDgUpkhRu2YZn7LamGTNntivL7h5qpump": "SUCKYP", - "9wBNNCXg8pb4SZpTY868CEr74S25Qy1JmGHFwo9F37dC": "SUGAR", - "CmRWGQAdDgDb7dPyDqUZNagLg2s4hn9tUeEiyFfiQgjZ": "SUIDEPIN", - "55bfLJQeYwPdUui7W34G4Ka4BM6Pg3DxZNPZfeJJj3hz": "SUIRWA", - "XspncLEk5B19BMLwvC26DqNtJxKWWxuBKq9phbCG8ff": "SUIGX", - "4DxUsKzkzPEsPd2QFrba4VYK5wQFKTEQia6TxpkFnubm": "SUKU", - "AKtixLfY2z51yT7G4yxYDrdjL786dz4zXynUGJUbpump": "SULLY", - "FnLsoqsoPckvPrSCVX3HZwN95jjRH4JSLnsPKyxhMhhT": "STOSHI", - "AGkFkKgXUEP7ZXazza5a25bSKbz5dDpgafPhqywuQnpf": "SSU", - "BmjaULzZoEKnGpwGMfdCSEeTio3giS1qgbGBnU5Gbonk": "WUKONG", - "C64cB3Ji5RgyeLz3YQmbYTzWRQ2w8X3jxW1B9V7MpxjC": "唢呐", - "zi87E9xtFPRQ2o9qqerFxUDQLZgEkHrhpHDxxZhYi9a": "SUPA", - "5TbDn1dFEcUTJp69Fxnu5wbwNec6LmoK42Sr5mmNmeta": "SUPER", - "3oNLZjLuqdTAS9iUEbUNdqTP7USvh65EVvdtoDabpump": "SUPERCOIN", - "4kVZLRtcPvzngu23oxXqH4MyyRMaFyh875N4g4dWpump": "CYCLE", - "2G8LH53fcr3aCrEsmAo73eunbZRbyjKrGH5qmur6pump": "SUPERCYCLE", - "GcmsHHG41giJYACYKrdxZp3kdKMZfg1UB7LaYzCzPxLJ": "SUPFRIEND", - "B9QxfiMd6tjSGnJ8nzFK3XbtuQNPKCNkQeehMqgxpump": "SUPAI", - "jLca79XzcewRuBZyaJxVxuKpUHcEix1X4CP1RP9ondo": "SMCION", - "HmLspvjpQtQEnArUyJoBSFGS38gNJwBuxAeqSV9SZ66K": "RARE", - "SupreByajmUdeJGLzvUEUm8W4xv1gF8JBqwYnvG41Dp": "SUP", - "SurfwRjQQFV6P7JdhxSptf4CjWU8sb88rUiaLCystar": "SURF", - "Cocouo2otHPBSov5KCjqGmqQC4MvxY7iZ9qWnu23y88o": "COCO", - "3z2tRjNuQjoq6UDcw4zyEPD1Eb5KXMPYb4GWFzVT1DPg": "SURGE", - "9mSsEqTCDEyFnPvJVdQ6K7uapfsvHahqGEyPV7zApump": "SURREAL", - "NLMYNpjYVnYnq8rjwC9rKXgpMhg5MtQUUmwVBiAbonk": "SURVIVE", - "6Hi2PPiqkwYBY2vxbn1oasbTknvsWRZvNL3PjKZnpump": "SUS", - "ChVzxWRmrTeSgwd3Ui3UumcN8KX7VK3WaD4KGeSKpypj": "SUSHI", - "ABDoiSudvFjjJ25rtXn8V2b6QGrH59KN6thAbsPvpump": "SUWI", - "FaxYQ3LVXP51rDP2yWGLWVrFAAHeSdFF8SGZxwj2dvor": "SWAG", - "JBSVUpKgYNHt4GLtNebQxTJmZgftTMWENQrziHtGpump": "SWARM", - "Hjw6bEcHtbHGpQr8onG3izfJY5DJiWdt7uk2BfdSpump": "SNAI", - "APbVLtq66bKBpUUdncHtVSab9TM1UrcqZ9wodXzmpump": "SNAI", - "74SBV4zDXxTRgv1pEMoECskKBkZHc2yGPnc7GYVepump": "SWARMS", - "9gyfbPVwwZx4y1hotNSLcqXCQNpNqqz6ZRvo8yTLpump": "YZY", - "NSG17kiSApMumWjLbZ4BLx8biPaAxTk9tDCdy1Fb9ia": "SWEAT", - "5SwxhEunuUUcWe4ojybdDwky6dpLxAehNmF4AA71STNh": "$SWTS", - "6rCYcfDe7o51YRwfkCMBTR9tgMRUhkehDZBsX9gApump": "PLSR", - "3dQTr7ror2QPKQ3GbBCokJUmjErGg8kTJzdnYjNfvi3Z": "BORG", - "SW1TCHLmRGTfW5xZknqQdpdarB8PD95sJYWpNp9TbFx": "SWTCH", - "7R88oQWwoFwMqb2ryrozDMvN3jNnWqrMB3ytKxNybonk": "SWOGE", - "GAehkgN1ZDNvavX81FmzCcwRnzekKMkSyUNq8WkMsjX1": "SWOP", - "CUzSRjBvqFFq45mg6j9oyQrDxyUTHEKM2xqKzDkZpump": "SYDNEY", - "2ocYd9f3564zUL1j6zUi8FR6Rn4Yo76BvXoZwLYASYFR": "SYFR", - "DuidnHq4WKSeJx3m4QKf7xSfXXa7H9ocT39hFjMSpump": "SYLVIAI", - "Fu4jQQpUnECSVQrVfeeVPpQpXQffM75LL328EJPtpump": "SYMX", - "9txCZaqYaQ62nFVcEWhosWx3vTMcJMFP5YYXQ5yhBAGS": "LIFE", - "HfJvxwLQeHeiDz8e91RWSwMm3R1pLh1vyjLoSr5gpump": "SYNAPZ", - "yso11zxLbHA3wBJ9HAtVu6wnesqz9A2qxnhxanasZ4N": "YSOL", - "yUSDX7W89jXWn4zzDPLnhykDymSjQSmpaJ8e4fjC1fg": "YUSD", - "SNSNkV9zfG5ZKWQs6x4hxvBRV6s8SqMfSGCtECDvdMd": "SNS", - "FnKE9n6aGjQoNWRBZXy4RW6LZVao7qwBonUbiD7edUmZ": "SYP", - "8a3sEw2kizHxVnT9oLEVLADx8fTMPkjbEGSraqNWpump": "SYRA", - "H1jJwQTgcAzUZjAKhxS8dA6FY8VZyTBxKxUyv3yCrYA1": "SYRAX", - "AvZZF1YaZDziPY2RCK4oJrRVrbN3mTD9NL24hPeaZeUj": "SYRUPUSDC", - "CqMLnuQ8bVSiak5crmhA3PCrwwtKi7R5VzcxJDKArfYN": "TAB", - "FNFKRV3V8DtA3gVJN6UshMiLGYA8izxFwkNWmJbFjmRj": "TTT", - "AY1emMxqWLJnHE2HkQvLdLyVpZdsLzq4S3tx5whCNQoC": "TADA", - "4ELGifwr2jHtEaFZUXXvQLAYiE6W5bvpWYZbJTRSpump": "TTG", - "keybg184d4vyXeQdFqs4o99YsMg7xBthxTJ6Ky3ondo": "TSMON", - "4AdDFsG1xzz1L7zKGo2fbiqv256Z92u8uCcJCgYuTBLo": "TJRM", - "4mE8keRGoGskVfgkDwXLvADNirsav3sUWTHyTbHEpump": "L", - "RTb54gpqAx6RpLAHRGnqQ3ciQ845CHqhg21ZzEJondo": "TLNON", - "3xUK7cHo3Vfh7uQCTkS2GUhJJuXhg2RXgApNYKHZ1B5R": "TALK", - "H4EkHReWbjJpqNUiUeYNNZbNKLqLfH8Jt9MkceXGpump": "OBJECTS", - "CwZNvjtai6E3z8mTH31wNLqjdDfepdQYppYVrsVpE7By": "AGENT", - "tptmXt82GmGnoE4qDRgHiTGKMBCh4fCdxjjCBYbgpZV": "TAMPON", - "BvvhHk7x6jXgwWXnBhtXMuDCWyoNKoWSp9bMSiixMDok": "TANUKI", - "GUUFsneDFrEgYwV2ngpvpMdAmwFjhFknA5eL7ehkeFf8": "🦝", - "7dLJnm2NzHPMwB7mJL7azhyMLqs4ZzKYkkhr3ob72Gwo": "TAOLIE", - "6MMdrc39L3rMZkshRP9WSKe3trSdcbx5ywJmzJBanzqw": "TAOSHI", - "CdZuiJEgdwQVZBWZrd6MvYwZshsT5HvB6tJYAjzuUTAP": "TAPS", - "GdRUzaVTvJpxtovLoiNjKhAcAfrnmBz3YaZK2xEipump": "THL", - "DTTLrCGbqn6fmNuKjGYqWFeQU5Hz153f5C3pNnxepump": "TARDI", - "4vTkpiA8Ti1YsScbCWy8x3zkGGahJUVwgVCPgPvVpump": "TARD", - "GNeXbKnKuVRL6nYQP59GHXcsBjL5pR4b2tM3zVYVpump": "TARD", - "XsjXR5jf4d4GNpJXwLvEBTMwK3ZYro2qZGgvUCzyJrx": "TGTX", - "51aXwxgrWKRXJGwWVVgE3Jrs2tWKhuNadfsEt6j2pump": "TARIFFCOIN", - "Hax9LTgsQkze1YFychnBLtFH8gYbQKtKfWKKg2SP6gdD": "TAI", - "FKNfAwb8TmjYkj11V4NiTz4TgrLWTWgm2NRwAD9epump": "TASSHUB", - "4oERTdtSCns3Zrxv2HJ8fw6Y6rticaRcu8GVGTgXpump": "TASTECOIN", - "LX2mJPsutHkUc6iXNFgumSC6LVKyc66xpy2zDTZpump": "TATE", - "EjkkxYpfSwS6TAtKKuiJuNMMngYvumc1t1v9ZX1WJKMp": "TAURUS", - "8rLcHLi5UARNbWhKh6YNUB7NFuLph8Dv2qWXHxaHoPDq": "TIFF", - "TaxhxvQdW5WwxDtxmyDnXfJ9eJV7wacbgow2kDEpogX": "TAX", - "F5z4iCEsgDU3V4xdvQDykffjoDnePNprvVCjhbDVCDg6": "TAY", - "CPjDhhBpwEsLygNuczKcNwBPgMeni1xLomD48x51MyYU": "BENJI", - "XsqBC5tcVQLYt8wqGCHRnAUUecbRYXoJCReD6w7QEKp": "TBLLX", - "6DNSN2BJsaPFdFFc1zP37kkeNe4Usc1Sqkzr9C9vPWcU": "TBTC", - "Hg8bKz4mvs8KNj9zew1cEF9tDw1x2GViB4RFZjVEmfrD": "TDCCP", - "AMNfeXpjD6kXyyTDB4LMKzNWypqNHwtgJUACHUmuKLD5": "TEAM", - "527rGhm2iBD4ogEk89MX7SA3NdpCwyBZ3fA8pKaoHg6E": "TEA", - "9mAnyxAq8JQieHT7Lc47PVQbTK7ZVaaog8LwAbFzBAGS": "WATER", - "3BgwJ8b7b9hHX4sgfZ2KJhv9496CoVfsMK2YePevsBRw": "EPIK", - "GGV2LcQsvJc2oFZFESnWBVW5AFoMknNX9r31wS2Apump": "P8BCI", - "CnfshwmvDqLrB1jSLF7bLJ3iZF5u354WRFGPBmGz4uyf": "TEMA", - "2gRVFdNHXnDZwCusjRQwCVuhuZFGQxFwqqUPjjhrd1kg": "TEM", - "CNQf3F7QLgWZ8367V9Pbw9hALutmvB23ZqxK7bsGbonk": "3022", - "XsBgbNRDujfznQq8P4NLKEkRTbPxUVt4v1xwkDzMMtQ": "TEMX", - "TNSRxcUxoT9xBG3de7PiJyTDYu7kskLqcpddxnEJAS6": "TNSR", - "HHoXk7WursT9DLBBhHzBWBbuQkfvvoNyHKpJi61mpump": "TNSR", - "exYfSJt6Fgfhfnp3bAD4roYy97hLF9npjYaLyEXondo": "WULFON", - "XsuwUbQSzCJN2wZabD1Gxf1MER2Ypa7hMzVMYB2WawJ": "WULFX", - "PZkZdFmVQbP41q2JKDim7ytnUzTscrvBz2zMH6fpump": "BARON", - "9MnKTgwFyXJgnZumHGT9NdHuzm98ACjkNwpLniLhpump": "FUN", - "BBSAW49Sru7jiSajWVKTSWs39psrjjHbMMtWGWTJBAGS": "TERRA", - "9vMJfxuKxXBoEa7rM12mYLMwTacLMLDJqHozw96WQL8i": "UST", - "GrLvp1v6SGUyDZxUDxy69cTibyTQkNNJ6ukePW4Tpump": "TERRY", - "KeGv7bsfR4MheC1CkmnAVceoApjrkvBhHYjWb67ondo": "TSLAON", - "FJug3z58gssSTDhVNkTse5fP8GRZzuidf9SRtfB2RhDe": "TSLAR", - "XsDoVfqeBukxuZHWhdvWHBhgEHjGNst4MLodqsJHzoB": "TSLAX", - "TSPXcLV76s6V2zDiZQ18kBfcbnjaE2ZzNT3ga2Pd99v": "TSPACEX", - "HdvZF538TerjMWc1cmtWVKUaGkS8d77sGH5QaDCCpump": "TST", - "4TyZGqRLG3VcHTGMcLBoPUmqYitMVojXinAmkL8xpump": "TESTICLE", - "A5nvoNHLmJ4ydL5KroxphCX4umFwcn8VWb4kXm7L6sXn": "TEST", - "GrgjbSENn4rVi8onexN7gKg4u6xt85f1kTcbtiSDtime": "TETHEGAMER", - "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB": "USDT", - "AymATz4TCL9sWNEEV9Kvyz45CHVhDZ6kUgjTJPzLpU9P": "XAUT0", - "Dn4noZ5jgGfkntzcQSUZ8czkreiZ1ForXYoV2H8Dm7S1": "USDT", - "8i51XNNpGaKaj4G4nDdmQh95v4FKAxw8mhtaRoKd9tE8": "TETSUO", - "81xLFvCzFaUM3KDxSHC75pXu3RPCeSeCbmGBY8aondo": "TXNON", - "8tdzC7mFKhmfPg3KdSBafHqH7jgi3CvKomfj3Hhupump": "TGMETRICS", - "6qqiEkw2vz4xoaFehAtrkF7mtxfT7m8BS2gSrvMrBLPS": "BLIPS", - "7yn2PRbB96TgcCkkMK4zD6vvMth6Co5B5Nma6XvPpump": "THALES", - "CrNchbhLnu4CzEjuc8uoxwFcaBgAYvY7RCf1vSm1pump": "UNICORN", - "DAtU322C23YpoZyWBm8szk12QyqHa9rUQe1EYXzbm1JE": "FOUR", - "HmMubgKx91Tpq3jmfcKQwsv5HrErqnCTTRJMB6afFR2u": "9BIT", - "EBGJEG6tohUxLWqfoo4c2qdYkEcrkTEWwXpq8LA1pump": "TATA", - "HuSyZB3ChbYngnQPSBWtmqceqKDUzghHCqppVf1Mpump": "TADC", - "51akgSdiNy3UvFqSu82wL2TNYPtXZJ1yWXGENp9obonk": "DREAM", - "766ivvadp4arnHKQ13RB3cD7PyvRDL42N2j7RCoMpump": "TAP", - "3We1AmjjZvNqvcL96zjyoQSNaWuejrbksaf7w4XXpump": "THERIAN", - "9cYXqd1ggyMTjwZFWnbDuf5EfLEcLuumMG67jRkJpump": "TAO", - "47b3pp5G7ZQJ15U1nEgRmorUfVTwrotgsFeyfdhgpump": "$ANTY", - "6rQFEuFNLZ6YdURoVmA1Np9ueNHr7jkTMYWnnsnCpump": "APE", - "6m2SeubvmKc1MGqGxQToy9i7tdbkxhnMxqgEhtgFpump": "ZOLA", - "FDQ77aHDgV6ozbv1b4WM5oXuHGV1cMnjSXpxvgSzpump": "BAGWORKOOR", - "DexxZWtZd8hdmeth9gKYXgTWa1VYdMVrWFBZ9RXXXhBn": "$KEKEC", - "AJqpoLhgr3rMpXAPHsmnKashBZVrnuo9HPd1Sa3Gpump": "BASILISK", - "8ruqZuRQJDHoMxCiCSqm6P41YSxRL6JNNKYiigKePVnn": "BWE", - "Abub8cRJviGWsv1qWjkR1wHqNdYjRenoaZxroFxbpump": "SHORTCOIN", - "EKwF2HD6X4rHHr4322EJeK9QBGkqhpHZQSanSUmWkecG": "BIGTROUT", - "BPUDuDxG2xyeKyDQ3bqDyPvTmCjXado5ETcGKV1wKhhC": "KILLA", - "dTzEP9JU2NRDPuWtM32gaVKip2fTHBqjheU1APBpump": "BITTY", - "GQKEw7RyqQKnLmwo6UwQ1x2pd7BYQSC8Fzz9aKoGmUV2": "BITCOINWHALE", - "28j8MCJqvhaW7oAuzfWJQ7SB827A3EHXwrfLexjGpump": "BLACKFISH", - "GTbWsjDBiAs7NtmqsaQZ4Qfwdnwhsf2HdMKTaha4pump": "BPENGU", - "4rbQrrtXcBgK7ohsy83Y58pEbybt2YywGcTT8vpbpump": "SHIGGA", - "8V29cUbz7MeF16rBJhkuGMxByZjfjDxcuqQsPb8AchmG": "BLACKSHEEP", - "FzLMPzqz9Ybn26qRzPKDKwsLV6Kpvugh31jF7T7npump": "BLACKSWAN", - "8y45AJzCUBSZL1UDFQRzCKovQBLQFudBrpPeg5yNpump": "BLACKWHALE", - "7uCHQdxAz2ojRgEpXRHas1nJAeTTo9b7JWNgpXXi9D9p": "CATF", - "9V7jznWgdN6tjMaJ6Bq11ZVQMkza6Zh45atgXbVmpump": "BLUEWHALE", - "3pKf5xsDUU5BTWzDUaS3XfiGPi1ihcvoP68pXpU1bonk": "PROPHECY", - "7qzGBsStxpQ6zBCzpFbfFBbtmmpgqdyk42vJXgzApump": "SCRIPT", - "3G1yVFfKzZxsvTnAnJMQzZBtyTpfWUTY6G7685nRjups": "BUBBLE", - "GdCN7rQA5egc8SU2Bc2kKB2kMwELneQZJVSt8gGBpump": "CAGE", - "DRTeDJXZYYZxmq3tmgSPvLi3ef5E51cRH1KzzwXvC5M7": "THECAT", - "CuM7jCNGFLQpFfnV7ZeUUbBpRWnCsPgTaNj6fgq4pump": "THE", - "21CnrFRqvEVkQZUTFmTXjcsJTLZhRY51ohoaCPwRpump": "DOG", - "DzMw8nmA5rnoRTTXGHCZaRp9EkMwG2anqY99XxiXpump": "$2", - "DcRHumYETnVKowMmDSXQ5RcGrFZFAnaqrQ1AZCHXpump": "VALUE", - "B9u8h65uM1oifqmP82VyUDb68iG2fKfZiyubmNMtu3h7": "DOG", - "4JPyh4ATbE8hfcH7LqhxF3YThsECZm6htmLvMUyrbonk": "EAGLE", - "2BUZ19fT8TYvPzhuvtCCp9ceu9eNRCmY11S4vSATpump": "EAR", - "29TWWr9Lk9wr4yhzNvJb7qgrmftTzvHgG1C5AKwspump": "EGG", - "93s39pnRwp5NUxvJ3A5zDWoXxKxqW4RCVzMuNsKZbonk": "KAPPA", - "DD1Nxg9KZ9C2rBk96iMLkfJryttzK9U4wV936C4Qpump": "SHITCOIN", - "BqndqeBCNSEftBKmbTbLVx1RX5zd5J3AGL9sG55Jpump": "Q4", - "Bg4M3QWtpi416fb1njZrXBmusBNav1XsDRc6izxXpump": "FIRST", - "4LK277DuJkKta8j41sXHdnvhmH3LLYwMjezXJE6jpump": "1ST", - "6NhHMW7cXLJAhxXGfP3FAuvEdbrwguZY2tcPJfCPpump": "TFIA", - "GbqbsRyBHVPHEv7xLEsikXmdpPPiApLTJesDBbL5pump": "GIRAFFES", - "DkETzNyP4oST2NMU4mmnZwxerh3EESaxtfczk3n3pump": "GOLDENWHALE", - "5AJELeDo5ZYwMbt6kRPp6hZ2xrcomwd7LuPXR8wmpump": "GRAYWHALE", - "2FduXi5zPSyxWpvcxBNsu9NcukhKmXaRpy4RLv5obonk": "LAND", - "LdRSv8xtxhykB4CehiHB8QG6iyQdVVj2PVS1QCUbonk": "TGE", - "7opAGer4hk78N7VtYjk2bVif6SzvPkBm8PzQAvzspump": "REPLACEMENT", - "AkgVdEH24cWSdzTf735SWESjsz4Y13m1u6AZcEmfpump": "GREATWHITE", - "CWsrReZdBGUxwVzLrZZ5U6n6ofzuGdJQ3B9pRXCypump": "GREEN", - "9CrY7PsMPx8pHaFKFV1Ty9CBp5GrvVT4dAanZoJHpump": "SHARK", - "6kUEk2u5oosdNaxabkUj4zn5XtbQ2L4vutozucXJkKmR": "GREENWHALE", - "6WUbAGBQd51XFQ3gagp3vJRsaz2Euo4PsE7uYvTLpump": "UNBROKEN", - "J9cEMoiv88SgZHoUdBqg3TxKbXZZzajJEqJ9hTqNpump": "THGUY", - "CuPrcDMz7CmzMeH8oGpmPXCTQfDmit2pgEE9UM1Dpump": "IMPERFECT", - "EbtkxtLroYojLHZbpvnsd8A83FnMZD5cFyf2iZfxpump": "GARRY", - "8snfTBHqzDEFpUuTjfibEnj5RnsAYUNh6o9tYHPAdjks": "INVESTOR", - "iGG7XyyxGKSR6x5j365S9kcSy6o5kDP3XdqkuayGex6": "IGGT", - "Ai9JQd8t7PdD3taL59kvfSttL71iuJeU5T6cND7kpump": "JAPANWHALE", - "4hqxNjEcyaPfyjQQCtc9JDWaGQ4M1nDScCMZ9TTwY4AX": "JUPCAT", - "D2ktFF1jxG52XgnoNL62veTvEHPkui5syBifjJeapump": "KNIGHT", - "WchjLJqbq8AY283hoYKLs7ikBVYbnxUD8aHZf5upump": "KRILL", - "5JkXrk1UV9xox6giRrFyELB9HnBv49QQXYUT5eHMpump": "QUIT", - "zGh48JtNHVBb5evgoZLXwgPD2Qu4MhkWdJLGDAupump": "RETIRE", - "EoP9nKZMtTFTWVjkJVJEQQXAgWoFZwgVgoKspkrVpump": "CHUD", - "2Hpd5tfRdF71sERDoSKtjGGti9nPiCNEBi7Q2f1ypump": "CAT", - "8NfK7b9u1RvMpHJnAnZki4mNQwjhvzrVZs7bRQatpump": "LION", - "tAjP8NfsUJDp4LcqmAPMRzhhD9BRpesGvuosSXypump": "ENGINE", - "A2vb8eA7HiRpb19fA2Dxz3XT8Mb1XxuNC6Su4aThVeLo": "PUMPATHON", - "FLeuXXyxKqrDU8MULuxLSPPWpv1TLroMSHM6XsG8bttN": "MEEK", - "tdzUKGeAchLRzpYBzmBWEGaoP7E8EQ8hPv8Eqi8pump": "MEERKAT", - "BfZm2yBtQFYnV7fLFboEJZbVDGcp29n685qVmgf5pump": "MEMEGAME", - "FBjp6EWSurnP2JX1h59waGwdnukLn6kik7fkFotSpump": "GRIZZLY", - "BERrTba2FLMEzxzgcu3XiuZ9XopVdPKmDmEnmZUfB6B7": "TRINKET", - "2ChusVZzi6pUqRyHnNWq27EczqUTacFYjSPg5RiDpump": "MISC", - "3V5CzXmTyyJPHDr6fYGv223PkWgaSddXPbsqrH5Vpump": "MOGGER", - "7TBD7QD9kyzwADErHusNtVWzSBdtG8j4aYJuXadNpump": "MWHALE", - "85ioZLv1JYA7z8YxAQ7PXeSCFbQWfn7J5L1ftbxQpump": "EGG", - "GASfLqDbE8c9w8mjKknxaHeDMmdy9usGwM1GwzVJpump": "MOUNTAIN", - "GRzrGzVmsx42j8o1TDmQ5uNAf76TQYb24HgvKaNwpump": "NORMIE", - "DHkguUzSuKRAZQTQ394tfQpa8CqrFoKSGggBr7XsawJr": "PRONUT", - "3dYXT71bZn3JAmpzAYfJsDoyM16pfXFVeUPFDK9Ppump": "41", - "BndKr3tLyEYvbarxrJNki72CnaFmPKYCSdLpDGuepump": "42", - "9AvytnUKsLxPxFHFqS6VLxaxt5p6BhYNr53SD2Chpump": "67", - "fmGLjVuQfBLbWPo5zxc8sSss3ixMW3u3bRP95uGpump": "ORANGE", - "9qY5Bu9qLnrddN8cx7jtQim2Sx8ywUszatUp3E4VJn6a": "$GOLDEN", - "D7fdJwFg9Ykf4tL9GgiSAugxvbEXcFZRhJ4FEgr5pump": "PENNY", - "2Pp6ebUvEL9YRTauUTmGTwYZKRfyQXGM9jE4S8WPDtEy": "LAB", - "DNnzSTbL8z4x28ji2dDmZKnYAbLf8zcmqiG9UjArpump": "PROFIT", - "5dggsZWR3YpqkWbKmo8WsDpTvmrxxokscNmnayXABAGS": "CULTANIME", - "F7Axwjz9fA8th4EUB6NbvaeJY5iVpAnrkzeztSxupump": "WHURPLE", - "9jUb38tuMqsCe328tM7kxFEm3wRpbo3MbXPJoFiTpump": "LAND", - "3CZNYQMF8wJdf37FGsjgwVWrRCKVbfZDSZy9LSXrpump": "BENJI", - "Faf89929Ni9fbg4gmVZTca7eW6NFg877Jqn6MizT3Gvw": "WOLF", - "7nY1cY2zoAeXmtk3bsQgmy8eytkAkd5HrHAGP6zxpump": "TOOOM", - "HLRGoPcK1n4fmkowVyBNkVHRoiiCUL2qyneNqTUNpump": "RETIREMENT", - "Be78Ld3SpYMif5YgxM4ipJx2eGSDcAaaCkHUzcV5pump": "42069K", - "GUxKzvR7QmvA5M5j2LufNynVMa6hxjWkERDM7Vnpvvii": "SIGNS", - "EVGzmSnoMSg57cE73kPZHTf1hkp2n7CUSDtybJhiTWBS": "SIGNS", - "5dfHpiBxagAKGUMLpCM246qHb8i8gADE3xdpVnDKpump": "SHEEP", - "T699bgtXQw4CJ59rQ4VzLsupVQUzoL5RmuhHnKrondo": "TMOON", - "Xs8drBWy3Sd5QY3aifG9kt9KFs2K3PGZmx7jWrsrk57": "TMOX", - "2UfBjNeDwzZsoUnr3hNKF4vkzAgiGmBNYCAxLq72cPa2": "THEROS", - "2e4hookwoJmyAReeSRcTP5dE4myMnwfVvPSztDrjpump": "RUGGA", - "ETe1Q2t6xRWeuv2s1PMHMHkN9UY8kbS3NTnZMDTspump": "TUTU", - "4nBQGUjd3oRSEQCkbRuBVsz1QA2XuJuXJwPWvG5Cpump": "SAME", - "49c7WuCZkQgc3M4qH8WuEUNXfgwupZf1xqWkDQ7gjRGt": "SAND", - "AnKdvsKiZSCVWw67AcuFcwBmmftG8JjhAgaShXnopump": "69", - "4HhWcsfjaWYKJtjaxd4EyGSAL4Ky8C3DvqAWPHnrpump": "SHAPE", - "2zrHZZGwouwFtsh7KrGe2p9gi97Wb2ANtPKJbf4Wpump": "BELUCHAD", - "2TAi8XLzDJ8btNTQp7WtCvhGuMnWzkFddr1PhYf6pump": "SHRIMPS", - "7ZrGwnTE5Sg3nnTWoeLCykEDwipDqMzJjUvEMsPLpump": "皇帝", - "4btUt5tQrN9MSfiHzZMdHf2ognjQZrWErjSJ9aiApump": "SINGULARITY", - "3GXrVnWfE9zggQdNtAVtzaKSiJZUzYPcudmPGhBvpump": "FRANK", - "7wUwkXo8Qjt3cYM8BaHHHeyfDY7ZSn7qvod92pNupump": "SL", - "Euoq6CyQFCjCVSLR9wFaUPDW19Y6ZHwEcJoZsEi643i1": "LAIKA", - "8zrgK9eADL7fc5GSn8seJ4n9E22bn5a4o5JiAptVpump": "SPELL", - "4xxuwsjvynx5CrudVpY8DaHjGQDFA2QkY4oQqCrwpump": "SPERMWHALE", - "H8xQ6poBjB9DTPMDTKWzWPrnxu4bDEhybxiouF8Ppump": "TOKABU", - "4sTZPBPzKuk4n6QqBSmPJLKpvucSts4hnbZv9dTppump": "DOSE", - "HjjmgBxtnu7soQfbyQAi3ZJAo8MPAA9rn6EPN2K9bonk": "DALE", - "ABTZw146nJeRBbsxHqxtZasNnwMJBKaYrfxVk6zSJGip": "RABBIT", - "5CqfXex1knfRiozwDtgFFNaiGR9TsmSUcWDNUTUGZQru": "THOG", - "2kaRSuDcz1V1kqq1sDmP23Wy98jutHQQgr5fGDWRpump": "ETH", - "8xHAL531iDVW9S2xsrWAFFEr1Yz71rR7RL7qPWGgBAGS": "TOILET", - "4z7secBe41i5Svtotp4k2FsjMVV6xykEVnrD4kdFpump": "TRENCHES", - "8RR17XBRY8v9tA9vuuFwrFyFEK5Z5X55Yj7AfEjv7RzL": "GREAT", - "ChHeuhbXXZ1bdewMmMuZaAQ3WnzEtVsDcoY2hon4pump": "CHARLIE", - "vSoLxydx6akxyMD9XEcPvGYNGq6Nn66oqVb3UkGkei7": "VSOL", - "Aj1mSpD4vJDN5r3xptnHsjHQgGWDLge8bRQi2W6pump": "VIBECODOOR", - "2KK4YMi24Tqo6tDn8mzYWkNdGjG3ufdXBjmLv7Fapump": "VOID", - "EtL967sgMgeDDSUrNhtQrbhe4T92idzqf4VuA6H7uGfQ": "MAYA", - "3QrGcwFSKXiKetD5ixzZa7H4zHPuTg8grrH7s5Mzpump": "WHALEGURU", - "2tJU3pMh4HJjKa9HN6HngdopfNqqaeEytFqW98Kqpump": "WHALEQUEEN", - "2Ci4XBmr67JFoS5YPrE8hmJPN9QgkcUab7xd8Vpipump": "WHITEALIEN", - "HNHNHcornzS5NsMegJA8wq2Ao68xVmKrHDRFif6pump": "WHITEDOG", - "Pz7zD359jya1s9jzp72inqfmKF13ziuJv6HzVfkpump": "WHITEDOLPHIN", - "5X61PKDGt6Fjg6hRxyFiaN61CDToHEeE2gJhDgL9pump": "WHITEFISH", - "3KDkph5Q9Cm3YUTmm39NgEDgX9jrRCHqkyNT2kMEpump": "WHITEHORSE", - "a3W4qutoEJA4232T2gwZUfgYJTetr96pU4SJMwppump": "WHITEWHALE", - "HegMoXeaVf8sUsHsqW8KEQtTzwHGZKuK5y8sMSCNpump": "WIZB", - "CbUPTbC4K7zdAEWxfa1nad4468xpX3LpWPhZVnzybhfN": "WAIT", - "Em9zr2tgSmGgRbz3kxyQeRXjRi9oc13wMu6cKam4zWFW": "NAMI", - "N9vEHC3EF5GxfPRAKsmpM62ubWGLCUNVdJnS1cBsexy": "THIRSTCOIN", - "C4wpLGTqMyAuaW7MUsEPdiUAgMo1WxpS8Eifwz1Jpump": "IMAGINE", - "9YJ2XM1KHYDUR3gnMQbEXtWyz7ZXTF1UtPgG8Hu5bonk": "SCARCE", - "49QYX4XDVyoyG7u6gYaktXeN8tPwx2RhA7wgiPvspump": "GLAZE", - "BdTEJq3yEp68SNmeBfqBbDDy7nbSGftkDhDkVef6pump": "FARM", - "DnPA3vTmXVWHzUV4XcFsRK84Af9XmZfcCdB8eitjpump": "TISM", - "BgpZ9cXE2dRMTUPhroffbMQm3q8QTLpRorpHYeJspump": "FINE", - "DocTwz3QhCgKy1CJJMruEAFEG5xTGoC43vPMkC41pump": "DIARRHEA", - "GZ6YjHeeUEVrtUo25LMqBCgX51MeUHHU46Dpeatwpump": "ALOT", - "DPfEwwrcWvc63PobzdcDkDbkY5FgWn7zCxEkXGGHpump": "WOMEN", - "D7Z2fUrxECBh91chmnou8u7E9Yaq7inzzcjg9G1Apump": "SALARY", - "EKCW975DWdt1roK1NVQDf4QGfaGTcQPU5tFD1DMcMe9Q": "THOL", - "3dQugtW8ne2PHcjoGFek2j8Ht7o87jKqaSPNWuC2pump": "THRT", - "GfCSfsnVE1jnS6NnSMoYqXWJV1ZgPug93J79byxYpump": "3", - "4Njvi3928U3figEF5tf8xvjLC5GqUN33oe4XTJNe7xXC": "T", - "BdNKUUo8vnvdhwi1Ejn4PXNfeeQF2374hCK3m91ppump": "THUMB", - "HUWzPAUYLgAMWbP8MiQctEUDjqgp5dzQDVw61xpA1Xdc": "TICKERON", - "YXUpFaULqhrLJS79JmFtAsNZQ2JDTnPemmdVZEFpump": "TIGERSHARK", - "DYUjm68jHoQFHMHzuRqomrhRcog9mc4TNrCWHpufpump": "TIM", - "ED5wbeyAYtLM4WRGnohPxJEwniaikEFioVmJyZH6K31m": "TIME", - "9LAjk5F4rFetELE4CygcBbZ5hYc2QhRrbJjfm5Q26jWM": "DAVIDO", - "BxXmDhM8sTF3QG4foaVM2v1EUvg9DLSVUsDRTjR8tMyS": "TIMMI", - "a788oktKgqmoZUQvLyxPN95sDWE7Wpu7hyPNmGDxfun": "TIMMY", - "FFMBajrDXwRwUw4j5D8XDN14Jp1Z72mnxjAoh34pump": "TINGUS", - "5ZMUboLJUeneKouqrqu4jeLE68vX68ehE5qprV3Upump": "TIRED", - "4WzLp3sV5uReVvxatfYmxoJWH7fY65QZBbcSAF76pump": "TIRED", - "2qqJLwt3DyYCjMVPKbfEmXSrHJqaVvaR4AbgfDXDbonk": "TISM", - "He4FyrMqmDBP2yUk3UL4HLmgeUkcfhy1gcXuZPxtheBx": "TIAI", - "FtUEW73K6vEYHfbkfpdBZfWpxgQar2HipGdbutEhpump": "TITCOIN", - "AfyDiEptGHEDgD69y56XjNSbTs23LaF1YHANVKnWpump": "TKNZ", - "HuPspkki5Qdnf5WAU7jtEThkeMhni6XQ23tunZRkZWUi": "TMANIA", - "3QTcwugiAGA7HnzcNf7qaHt7XakdscMxJ1TE5Wnspump": "TMC", - "pDY4GPJfZcNETPG7myXeafQfgJqqVkn81bMYDyfondo": "TMUSON", - "XswCi2U1G6Ppbw1QhG45yKb8UKuR1FKLJrquv2FZSD4": "TMUSX", - "FViMp5phQH2bX81S7Yyn1yXjj3BRddFBNcMCbTH8FCze": "$TOAD", - "2xMNNityqHbafNAgNBF95phKcdx97qahJMwkUh4Bpump": "TOASTER", - "feR4zPDDZzbPyAdjkj7mzDFyobX21HRtfocdTszpump": "TOAST", - "HPueqQjSgaSatMBKrvBvAnRmc6jnr51cPM1EjUJVpump": "TOBI", - "2k2Smcn2CNygC4gocaipRUMzeqnqPtGg3S6e72j13jBM": "TBY", - "CwjWKarykhGRoBHpeKJDRCo3Gsp6woxCQEaM8uSbjupx": "TCG", - "CK7UwtyYTo4CmBohCF9RhF6mCNDxnLMcZsCqouP9pump": "TODD", - "4T54PGF4gAU75NL9j2GHvrVbR6u5zjyJE4zGuJbkpump": "TOGA", - "Ch2veYHxMWBDgw77nxWvJeGz6YjBD2g9cm21fNriGjGE": "TOKEN", - "2zMqyX4AYCk6mgy5UZ2S7zUaLxwERhK5WjqDzkPPbSpW": "TGBP", - "3vZY2MKBzRMoT4nGUCWYzXPTES2jLaLNMV8Fn5cKpump": "TOKEN", - "HmjCoarLh5duURfJ333DwfFiPyTCgFT35pRSAoP8pump": "TOS", - "3Ws5Z9ZVWZFBFPQtmUqSbL1pg1LG4jpfpMHGkUWK6UGJ": "S", - "Dhu2cTaaCFnws87gh1hBMPcsANKoThjHhCBxcjgAjups": "ILY", - "ToKeRoWpbMa8bvCTDtyuJtVo7Pos8tgcnM9sVyDBmBS": "TOKERO", - "3SQSwa5PXDWoBjCGi5uXCgNZ7FDCD8gG8Ai5UkLppump": "TOFI", - "5C8LMqZ9dbQ3RWoe5pFk5fJPhgiBQtBYdMnzekfJpump": "TOLI", - "GgJJRwLg9NzFQ97o1CJLGLp1KLSUMBwFc6eQNVEr4fbW": "TOLY", - "GwyxednWbrhgT2K6iPUsbtadErA7TBGqsJjyzAody2mv": "OPPIE", - "JCeoBX79HfatfaY6xvuNyCHf86hwgkCCWDpEycVHtime": "TOLY", - "9ekm6h4pxZcNbdyMw5fWkEnqAStjQCSzZ3TEfZ7tpump": "TOLY", - "totuNukof5QgTjr6vW3pNUG669kSuEoh1zVf92eArmQ": "TOTT", - "55USDUivJLe4L1kY64XK1nDPAqbphG6nRpTjk2iZ5PwG": "FATCAT", - "FiBSKnRpjNHChN1BANpr3dsVFQuuHhETMen4xUDgpump": "TOMMY", - "3Jam4LGfFxPZJAQEzruiBTt28bi1Da64QCaxdLaB7o4g": "TOM", - "5wU4tUcAbds7d5cmnGK2otHa9gbayYsD2mhz1reR6c91": "LUIS", - "2XsexjLapEenZobsrqXWZsDga4nNU7XmoW48NgWoGzht": "TONIC", - "2DSFvQtdoqv7DjPgXL5tjTYJ3ABBnoBeTfAJTXNdbonk": "$TONIC", - "XscE4GUcsYhcyZu5ATiGUMmhxYa1D5fwbpJw4K6K4dp": "TONXX", - "9EYScpiysGnEimnQPzazr7Jn9GVfxFYzgTEj85hV9L6U": "TOOKER", - "8NH3AfwkizHmbVd83SSxc2YbsFmFL4m2BeepvL6upump": "TOPG", - "AxGAbdFtdbj2oNXa4dKqFvwHzgFtW9mFHWmd7vQfpump": "HAT", - "AqHsVVzWuu7e1de1j7sGJ4MdJ74xUvCeXf19Y4dLiM3S": "TOPKEK", - "4mWTS6KztDEoMu2uqsnbgbeGRh6chjq7Fbpmbr1Ypump": "TOPLESS", - "5YqckGEkWHJmp9LW5aUF2uJrdUbMJaHcYnGjA8M7Ebw8": "TORSY", - "8WzDFPNjUSV7ZPpNiqKY8NcWH9J4tDSgSeifp6Kboop": "TOSHI", - "6jfRbgs3B1KrhohSZ7KbhJckHZektQs1rRUSwWeZpump": "TOTO", - "5314GTpDziP2ZdaANnt5KJEABGXy5Nn5Kyc3SFPYpump": "TOUCHGRASS", - "5Ro9KjSUGYisjREz8K5uD1fMdXnu1Jfq3Ktqm4EQMc1R": "TOWER", - "kbmF7ERJWMaaDswMprrH9gHSLya5D2RMBNgKqg3ondo": "TMON", - "XsjQP3iMAaQ3kQScQKthQpx9ALRbjKAjQtHg6TFomoc": "TQQQX", - "7t1T28iMh5GsW8oTMx52x3u3M3hDbaY8psbL3rZ6BMrn": "TRACKEDBIO", - "39foCGC9zixPSXVzRZdCbKE6nmx5WyjDq5FJ3J7x4REV": "TRADIE", - "F1n2Tn7Eb9jTbSQiqy2Z7G4VTbkreHGQqcRKKmwZv726": "CFA", - "DhuNPyaMF7n8rYs9goo32bcptkoo5bPhWBF67ftLVjsW": "TDM", - "8Y5Biziz3njt9NWc38cJuL76pdfyrvX6SnjtFPeDpump": "TRALALERO", - "5ypxFmuJUTsSkAeKRuRs4xSxkb4vWbjMLT3GY7ckpump": "TRASH", - "C7AxK6ZdbmN9RmLnmV95vE2MrmAU77ftQZz5RVhcpump": "TRASHY", - "4Fr2LL7tJ52Y6a8g63nHzNqWDjy9Te1H55m8rrX7pump": "50STATES", - "7ptr39shwummyaSku3i7nKkJtBT81CA1SYsvSYTyDUnQ": "TREASUREDAVID", - "9zZWnw2DbJyxFZNa2Qhkh2a5zKDXmXnucdcTCyMkbunt": "TREASURY", - "4GqjMwnXb4pm64CuFDfovFMg2RCo3nPbeFUfsdAupump": "TREB", - "J7hX5qVuJuiUiuZ1AkqE7eVS3sjTJ5kgFnuJwR2Rpump": "ROOT", - "D3jYB29ZjSRwry6c266VNoeVkQWH8fqSsMCy2F8xBAGS": "TREES", - "5AzTQ5zUuWUNGetxaRpo9DK9w8FQXoPjn5a4ZXMMmoon": "TREEINCAT", - "HLnTNCG5RD7jYVduFc1pMCHiuApoWGn9LveqEFanQFZb": "TREN", - "AaWVpk6eZbgBfVbq1UfXw3EXBmAvw4QXov1xHuG7pump": "TRENCHAI", - "GACpABn18xqiSJbD9ZEyArJDT9RHRMUut5nK9Z9Spump": "TRENCH", - "E2KiYxrbfeBeajPr2kJAvwkzPr8p1hi81kFGYfB6pump": "TRENCH", - "8ncucXv6U6epZKHPbgaEBcEK399TpHGKCquSt4RnmX4f": "TRENCHER", - "BbbwE8rudhjK4husSRc37X54mYyDBRcykJ7fk5oHpump": "TBC", - "EU1q1FfzCPG9QHZZQvy1fL7vmisWtBApMcaiaWsVpump": "TRENCHES", - "BzyKa1FGjs2EUpu3GGDibY4xdygn5evAiRboKmETpump": "TRENCH", - "t3DohmswhKk94PPbPYwA6ZKACyY3y5kbcqeQerAJjmV": "TRD", - "3ADvYyTbmQcLMiqdFSo1m2KpQWxYg9X6wW4N8viRTNf9": "TRIAD", - "DNJ4zTzuLbt1wXZpkBNwbgS25RBNaqdKHdKzFsQCpump": "TRILLIONS", - "2tV44KtbRfYv9NyReESkVNVxenWJQQMHuWuix1eZGQu8": "TRILLY", - "ArsdpgcBPYRsgjsC9NZn7ikQVWHAwkEMdat3AUhE3io": "TRIO", - "9PMjLqd8zPdKkJUXarnit5t7tPL3cCscwHzy7ATondo": "TCOMON", - "BLDiYcvm3CLcgZ7XUBPgz6idSAkNmWY6MBbm8Xpjpump": "TRISIG", - "9eXC6W3ZKnkNnCr9iENExRLJDYfPGLbc4m6qfJzJpump": "TRIT", - "5UUH9RTDiSpq6HKS6bp4NdU9PNJpXRXuiw6ShBTBhgH2": "TROLL", - "6gTqf9JFiCK3wPg5pTBzWY3iMkH8z9SbuJX8R2vxpump": "TOBY", - "5mMrEEDRyFutXr7GB4NdJLkhRDQzrK5VC9Qc3Tr8bonk": "TROLLGE", - "492JGSNiNd3DvWtnej1raJwNQ6Qui1W2UaGtVUCPpump": "TROLLHOUSE", - "Cqy3AkBqP866vkw7p7ioK6h6VTfQT6tFUW1dzUezpump": "TROLLICTO", - "ACHEprPEhxibTS5URUtTb44NFA14wpb7vHoTu3rZpump": "TROLLINA", - "C2i8WUjvkpZPXEBumF6dwFJakaDA3hTYCcBpfLMgpump": "FAPGUY", - "Bj8yqpGGJ4izJrUtUoVG27vTBDVaUYkWGfhoqSwBpump": "TROLGA", - "XsRrH4fDA27xfyDjpcwefvcBdHCXs34krUuaXGEXzqz": "TRONX", - "Trove4jcQhE1dZGrjBwe8LUEAZizcFDZ35gUHezmjhZ": "TROVE", - "TRUFoUAg3rKEtcFxxeeuVm6Dc4Er55McRXMSgPk3hZ4": "TRUF", - "5jJNLgegAQV1CqzLLAXFPi8qzjFcQWvKaADLDdFkpump": "TRU", - "HdHqKPz3n52e6FCJREKY3MS56TagyvRxsxVYG7E4rF99": "47", - "TrUPjEqGpUph6sMJX8C3yYja9u4RcVUGTCkGG4xLrjG": "$TRAIMP", - "HRw8mqK8N3ASKFKJGMJpy4FodwR3GKvCFKPDQNqUNuEP": "DJT", - "7KuFdcfzbc3LLoRHNRWfW1dhbPNzuCv41hyYbfSUpump": "TDS", - "HC2KyVkPK9Mc9NEFPUi43okhTYPa5fStk6a3Ci3cDbQS": "TRUMPIE", - "QcNnhASrCxLpkqndMvLJmBu8HAkdViZzdipBAoypump": "SHIMMY", - "ABmMqCr6ybUhSPoG2iT1JKaf9ix5dqJU4eqmaBBCUipJ": "OIL", - "3nGc7DR6jE41W2iLfvPC3ctPEPzhqAMWuvZRKXNLpump": "BULL", - "DueNdyyx5a1ZMeaZr5kSSK3BjM5ADnDmhnD8ya4R8pGE": "SHUFFLE", - "6bda6LhSpg59yQoVSvw5yQ6rd3TRcFANQPjhTLfpump": "TWRAE", - "2MDr15dTn6km3NWusFcnZyhq3vWpYDg7vWprghpzbonk": "NOM", - "CckLtcfEtsYzg8sUrVBBrFKt16iExpvEurc1dhKmmoon": "T3AI", - "HoZsDYnsna4LJJoQE7yqzrRTSj5eDhe4hFecqmawMeRW": "TRUST", - "u29pRsBVi7vPmCYsaYmtJEghPZFiWUK545QZh56pump": "TRUST", - "F5sc3CGLmKueyUqaCkwopo9u61HsgpmJc8Cmc9xibonk": "TRUST", - "EqeEBGHQhQy6SqeaJcnqAsNs3qaG19sdF89Xsarpump": "TRUST", - "6zBkvjLkLZQxRBqACM4nHPjjdrjYjL994eruJtTWGJWM": "TRUTHAI", - "FMzbxgGBQp2DTqzBE5KCpb3srd7oo4sUQLLoV3kzpump": "TRUTHAI", - "2GmUPhpe93kcTJZrC7NJ2keeDZRT5dBveUgegb13pump": "TRUTH", - "HyVDEc81P8DGfNfZbBZwyntJPvjrSi7JEK1vgqbJmUq9": "TRUTHFI", - "CZZSiF7CpqZmBrLeV4YbcdwDFUnf4vUF17PTTKuipump": "HENTAI", - "XsafvsGtzFqqHgTnA3aPC83EAMkacU5mcGtcSayhpVV": "TSMX", - "4mbdysBik3jmzD7mt6FGPDsMxnYcxExSQRFjPucdpump": "TSOTCHKE", - "9yNykgy7xvXv9ij8TapMjXREArGCREjY38KoFtASpump": "TSUBA", - "463SK47VkB7uE7XenTHKiVcMtxRsfNE2X4Q9wByaURVA": "TSUKI", - "mntQLkYmUJ2BDw31ypEoAeE251uXBdPVTDVU9s2DvWH": "TTBM", - "BsXmXxfwQWks6WHtfmo5q3Ji4mQieEx2WNWmUUe1pump": "TÚANH", - "L2aKgxwNSGn6i5njrDqtFxan82uidsfjXHrYNSspump": "TUFF", - "9fEXUmPnDJYAXpoZ9nWi1gQZPwzVKdf8g9fYXW6Epump": "TUKI", - "GfLD9EQn7A1UjopYVJ8aUUjHQhX14dwFf8oBWKW8pump": "TUNA", - "F28UWka8PSyG1jUtVZ2CfFdF1dkLEA4rw7GkFBW7pump": "SAHUR", - "J8PSdNP3QewKq2Z1JJJFDMaqF7KcaiJhR7gbr5KZpump": "TRIPLET", - "2Dyzu65QA9zdX1UeE7Gx71k7fiwyUK6sZdrvJ7auq5wm": "TURBO", - "7Cx1fMabGWgm86xvv73WzSAcwtLppKf39Ywe8e6Qq4T6": "TMOON", - "F14hCmEKjcaXobNE2fMdRX9EcetC2oNuiZVjpce1iohE": "TURT", - "bZqP1p2mUd9ftH6AFBmoyQqsAFcG5mddMMh3Y8mpump": "TUTC", - "8r5ZuzAt5c8VAHVSMpgUJNzpp88wHLnJCsLTLMdsf27Z": "INBOX", - "2h6EQ8oYnDQK4ppwHqR3JgmfRVpL4L6ga7AhaPnxpump": "TWIN", - "AB1e1rTGF8xSoYzXEwNWohuMHLCMrBoaSxT6AARmQksd": "TWOCAT", - "G3RdswoDRpEbWFCm2GYGQz42gnFWFF1jW6q5aKXzpump": "TYPEN", - "KJNeFW3kk3ycPjXpC6cbuyckjeYHacc2ekhtAi5ondo": "UBERON", - "XsAsZLF4MmsvS1sDxRMrUz7REjHfwbC9UAMXSRBqgEB": "UBERX", - "5JkQBPrYdRK7JsC39KPPbZi7r6uLx3ZuL7jjMf4zN4c": "UCF", - "HH8bchogQD71iuLghP4cuvSU7vsGJoMJDBxvWTFu7MpA": "UCIT", - "99myne1mVnsSv2kAkzZDfNnwf5Xb4SASJnaCRXdbpump": "UFO", - "9enS2VasgRxzT2QVkhjfVJAtrVJGB9MSYTSvUpf2pump": "UFO", - "jQxGhh5r78RVp8Q5yAcjoy6ing5tFx5BSHV2nprP3cu": "UFUN", - "74Rq6Bmckiq8qvARhdqxPfQtkQsxsqVKCbDQL5PKpump": "UGLYDOG", - "B3QLjWnMfFjjzaXj1WJ4JmUqgFPVEfKbuZmfeXnrUiui": "UI", - "CVcbbU7UBmY64LCDz466GftBC6Ph4RRyQZ97q1tpump": "ULTIMO", - "PRVT6TB7uss3FrUd2D9xs2zqDBsa3GbMJMwCQsgmeta": "UMBRA", - "kedoobK2qe2f1V8ee5vDXFr3H7gKnhQVNJXA3Ltcyai": "U1", - "3gV3t7Y9zsd7wNjGXXukn2RT81vfH46hA3oEcHgVpump": "UME", - "8zdFumGcK2iF8AcqfSEjaPX4NzPuP3Tyx7msnvcsBAGS": "UNBAGGED", - "Dza3Bey5tvyYiPgcGRKoXKU6rNrdoNrWNVmjqePcpump": "UNSYS", - "GgzH3GfJaZbkRt8zDbeKK9XYERSgEQUviXK7av2g1oa3": "UNCHAINED", - "FAnDkc4jfztmgXdxJmGP4J9HpgHqv2fZLbnk5te2bonk": "UNDERDOG", - "71QU3z2DoYKYLfpshufm3MJAhr2UJYXJAKmVSXSkbonk": "UNEMPLOYED", - "8MgghUHzWyCXSQb4ReqrYQZ9rL8cQNaZ84GsqWhTB4Uu": "$UNFK", - "FArDgWGo5ojumaA5BqFvGjGosZT1KaQM4qYBBYXFWNBo": "UN", - "UwU8RVXB69Y6Dcju6cN2Qef6fykkq6UUNpB15rZku6Z": "UWU", - "DzN1qkcRdsxQRFb9yVvz73Fnu6SbD3f5oJgArVdx7Nzc": "UNICORN", - "Bx3REmoo2J4t4K6JF83AbQTkY8C6G6sc7TyyaCe8surg": "UNICORN", - "eL5fUxj2J4CiQsmW85k5FG9DvuQjjUoBHoQBi2Kpump": "UFD", - "2iygm3ZBu3YRDiJREMrZTLayffBJBShTrhEe9iyYpump": "UP", - "5JDtsBBzPdpYzTBXw7GPJ6KQvy95e6D89dWPKeWWpump": "UAP", - "CkJgoT1YcPqWdjUbvqgPf5BDwvq2GeBX7REG9TCCpump": "GIVE", - "EvsME8gdnEwPLbTnhrGVDwrY35zBuB8hEGCq59Hondo": "UNPON", - "8FU95xFJhUUkyyCLU13HSzDLs7oC4QZdXQHL6SCeab36": "UNI", - "7Zhxshgt7Ft6pHFYMrHE1epWdWre7sJ1Af1GhEUnitas": "UP", - "kPBGL8vAwKN3UGmr9cjkM2dU79SC3nzTC9yu7F8ondo": "UNHON", - "XszvaiXGPwvk2nwb3o9C1CX4K6zH8sez11E6uyup6fe": "UNHX", - "CZEZmARqTUu4CpBcTupUNcLyDR7wnELpvn6cujQ4pump": "UXR", - "rCDpCrYepyYffZz7AQhBV1LMJvWo7mps8fWr4Bvpump": "USDP", - "8iK4qEEb41gkogyEUn4qjrnuj3vgvQEf1RcxXauWpump": "USNDC", - "rpydAzWdCy85HEmoQkH5PVxYtDYQWjmLxgHHadxondo": "USOON", - "3t1bNt1RoXZvrPDZp8SXEaaQarZRBFf7W6mi9Eb8BAGS": "UTK", - "UNTyTuyNPhLufn4K4SMsW6BxbvfYEMgQ4qUT9orPapp": "UNT", - "9psiRdn9cXYVps4F1kFuoNjd2EtmqNJXrCPmRppJpump": "UBC", - "uniBKsEV37qLRFZD7v3Z9drX6voyiCM8WcaePqeSSLc": "UNIBTC", - "5QmbJw7mM6tcCdXVy8ftc2bu8izded7Etc57TMA2pump": "INCOME", - "79HZeHkX9A5WfBg72ankd1ppTXGepoSGpmkxW63wsrHY": "$UOS", - "B6VWNAqRu2tZcYeBJ1i1raw4eaVP4GrkL2YcZLshbonk": "UOG", - "G4cPnwTLd6oKA6FQgm5ckJmw3tEYapQRmqpQEnQYbonk": "UWU", - "CB9dDufT3ZuQXqqSfa1c5kY935TEreyBw9XJXxHKpump": "USDUC", - "CasqeWMxfdfzhohWNA7VHFGkmTxJ8jkv5foinUBZpump": "USC", - "4EKDKWJDrqrCQtAD6j9sM5diTeZiKBepkEB8GLP9Dark": "USDARK", - "7WXaHLjatDZBAZ7hyRiFpYpGpPbcKiyHf6HaxUzSbonk": "USD", - "3vz82EWYv8xnc7Cm7qSgERcpMeqw92PcX8PBz88npump": "USDUT", - "5TM2nWGomk9MmBNKmsFf8QAsHZf3S8hvtu214KhQpump": "UC", - "59nzDoRyJ1QcLcFYFs4GVdeZ4qUEb16T2KNuHPWMpump": "UNTIL", - "9iQBkyLKtqRXvb6ARGvQydt1LYgGcTtVe3PTDpWhdPze": "UNVAXSPERM", - "4b1guAuV7xF5qD6Qi6AP8Buj15ff5v98RQNAaCGMjups": "UPRIGHT", - "Dd917ev4Zzde2BkPm31FTLseHNHamJPPUwQgGAybpump": "UPG", - "PeGV8nmE7BP23Ev5e3LZJ3muvRvVYAaYozQfHDSpump": "UPRIL", - "UPTx1d24aBWuRgwxVnFmX4gNraj3QGFzL3QqBgxtWQG": "UPT", - "F23fFqpRNsmWjuUrFpfM1pvoVvMSpLuN6hY978Y1JXLt": "USDCAT", - "6vVfbQVRSXcfyQamPqCzcqmA86vCzb2d7B7gmDDqpump": "UPTOBER", - "7VKG39KqGopTetYvgBA3WADEB7fiB3kMChqA6Lj8pump": "UPVEMBER", - "EYo8D3cLdF1CDeGms5M5VHyU52HJYinkMZ1cqvYondo": "UECON", - "32jSCpTYmLze3jj9ruqtLmPPBRtag9ud5YbsJwcfRcVM": "URS", - "BFgdzMkTPdKKJeTipv2njtDEwhKxkgFueJQfJGt1jups": "URANUS", - "BwTu5zJqEyLYawwYoGTWT7UxC85eLYQWRcta1GiEbonk": "URA", - "9hjZ8UTNrNWt3YUTHVpvzdQjNbp64NbKSDsbLqKR6BZc": "ANUS", - "F8W15WcpXHDthW2TyyiZJ2wMLazGc8CQ4poMNpXQpump": "URIM", - "GACxkcy9WDWHhB15oKLHp3PPzQV4zLt2m7dcGPoZLkZY": "URMOM", - "9j6twpYWrV1ueJok76D9YK8wJTVoG9Zy8spC7wnTpump": "URMOM", - "FvgqHMfL9yn39V79huDPy3YUNDoYJpuLWng2JfmQpump": "$URO", - "FMpA3eQbHFEPkkb9EYkLAddfs8TXZfz1Xq6Kqkowdaos": "USAI", - "6P8ixuqGZpfyHAxyxbU4a31vsMiFiCQjBzVV58gPpump": "USAID", - "2yFiCwdLiUfxq9PcNXQvu16QdgBFniCJP8P8gEXNpump": "REBA", - "BAU83kqEqhyiexfAMQhZZE5KnGogSqh17fJc44Sondo": "BNOON", - "USCRdwZP5UkKhJzhWuD7XjTUviHBtZJbLG7XpbKng9S": "USCR", - "79cm2rFTnRSjJubaAx3V2anc7uKiCkvd58RkkDRFCUSA": "USD100", - "USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB": "USD1", - "usdsfJbX78ktZUnoRC7dwvvQz7xH3WdkpGne76gdUia": "USD+", - "GPdJrAJa3Qrzwag5aKsM3bq6x3vFF3966QjTaSYXbonk": "USD2", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v": "USDC", - "8smindLdDuySY6i2bStQX9o8DVhALCXCMbNxD98unx35": "USDCV", - "A9mUU4qviSctJVPJdBJWkb28deg915LYJKrzQ19ji3FM": "USDCET", - "72puLt71H93Z9CzHuBRTwFpL4TG3WZUhnoCC7p8gxigu": "USDGO", - "USDH1SM1ojwWUga67PGrgFWUHibbjqMvuMaDkRJTgkX": "USDH", - "EJvBDsre5a3DbLmHZBw1YUeUqETJ3X9UkgR6TLiipump": "USDR", - "BNgsQdjfWmjoy3cw8T3VXWswHfgCzEMyQzUno8gmzmRC": "USDM1", - "USDSwr9ApdHk5bvJKMjzff41FfuX8bSxdKcR81vTwcA": "USDS", - "8yXrtJ54jZtE84xEBzTESKuegjcAkAuDrdAhRd8i8n3T": "USDTB", - "9ckR7pPPvyPadACDTzLwK2ZAEeUJ3qGSnzPs8bVaHrSy": "USDU", - "9mfqn25C1nMVwn7YjxdJFvvTqichHFf8aR6vzNzPSodh": "COMMODITY", - "HD3JBABeFkdZwUgKwhwJYqjLNrPWXEaDVfH4uMqRpump": "USEFUL", - "D67BSPqECWv8GEozzFT4KCMtT9aFkswK5fC3oeBBbonk": "USEFUL", - "Dz9mQ9NzkBcCsuGPFJ3r1bS4wgqKMHBPiVuniW8Mbonk": "USELESS", - "CDUnjzbb1fqAwo4KZX3u9GYRsMdZUfhTLVzKhK5pbonk": "AILESS", - "EJVceV9HmJpG6XAxnKYtTQFHf4S9ckgKwBsKMF4fbonk": "USELESSANI", - "5NnL7oRMtVW3ZqAt95gJQs12Fbz2caCYh9rgy1kQpump": "USELESS", - "Es2ipHL7qXBcLmZ4N7LP9PHBHaWaTMTAkxDwGGjondo": "UNGON", - "USoRyaQjch6E18nCdDvWoRgTo6osQs9MUd8JXEsspWR": "USOR", - "5Q8RSzXAybeLYkf76mMP5a5C9ikc8y4Qq9raJqKapump": "USTREAM", - "6FrrzDk5mQARGc1TDYoyVnSyRdds1t4PbtohCD6p3tgG": "USX", - "FPZsqAgtwf58GmJMctZGSu9RaXiGAmQLL6ZtKUUewm7k": "UTG", - "FwrcPjGNdTbBXWBKktRvnNTnAfRNtv9nPDGV3gjgpump": "UTILITY", - "HGTXnhgyast5fJKhMcE4VgyeEVWhYKEsHxpZtpjhrYqA": "UTCC", - "9gExMiURmgzKZyq5PhGnxaLcFrt4hYcca87HK8VSXe4g": "UTT", - "7kbnvuGBxxj8AG9qp8Scn56muWGaRaFqxg1FsRp3PaFT": "UXD", - "9raUVuzeWUk53co63M4WXLWPWE4Xc6Lpn7RS9dnkpump": "V2EX", - "GMcThHFBnN6zjB2Mvbgdi19A2m35g81y7B1AKhgGbonk": "VCM", - "5cL3TVJ7p5ZKqyx16DXwpdcNx5u19vQtWujA9vYindi": "VALAN", - "5vSuxSJhyGXD4wNTrXS4P1gWmLUjCc3zLkE7rE8Pbonk": "VALENTINE", - "Gs7yqXZpiPNMpQndq43dCJqRuBAaoarcY1uUbg36bonk": "VALENTINE", - "9GtvcnDUvGsuibktxiMjLQ2yyBq5akUahuBs8yANbonk": "VALENTINE", - "GX7jJ2E86ndix3B1V3Q2GVahukiDf9eUip2yBmPmpump": "VALENTINELESS", - "nn944oFMxsHg9AnEuBHWxtBpGjRX3DRxx86PseuDrPJ": "VALEO", - "growFDf9teg9gwVTTY3DgpPXU31qBrnbSQCqtY2vkR8": "GROW", - "3wPQhXYqy861Nhoc4bahtpf7G3e89XCLfZ67ptEfZUSA": "VALOR", - "7tKa2ui4KgDTG6KgggzrVYjnup9Eh6Yazdwi4UcRDMaM": "VALTORO", - "9x5CLPb3SeYSBKvautqpJWPjX9TUCVcWTS12Xawapump": "VCC", - "ucQ3VfWAx9pkCN4Kg84zE56FtB4FJN2kQH4ArYYondo": "CLOION", - "DnvbCqRuUYssmKVRBRNwkUnptHitH4ZZTt1KVuZondo": "OIHON", - "tiitb2Z1HtpB2DpVr6V7tdCFS3jmTinLeuGj9EVondo": "REMXON", - "34mJztT9am2jybSukvjNqRjgJBZqHJsHnivArx1P4xy1": "VBILL", - "F3dMJ9H137YUNc9cpN3gBWDSq4MSRbTFtojH65Uondo": "VNQON", - "jCCU4GwukjNxAXJowG2S4KCrr5g6YyUB61WHYvGondo": "VTION", - "XsEdDDTcVGJU6nvdRdVnj53eKTrsCkvtrVfXGmUK68V": "VTX", - "KuiYLPVq65qixD9TgvxBC576C4gG6vVTCdbh2zFondo": "VTVON", - "XsssYEQjzxBCFgvYFFNuhJFBeHNdLWYeUSP8F45cDr9": "VTIX", - "66bHtzcXqmLgkn668EG4ETzHL3RLz5ybdckqvrPppump": "VANKEDISI", - "GGd1WfYyQaJQZ2VNhqNcrQbXRnUYgTTWeG5gBLT8pump": "VANTA", - "Hfm5LMEHXAGYuq2ofT1opYuDeE6Eg4wyTydL5wZfpump": "VANTUM", - "2qmcZfyPpiFFx9kJPY9i7dM7Da6J5rVUvXzTsx1FJtfq": "VAPE", - "VAULTVXqi93aaq9FsyPKgdgp6Ge1H1HoSvNC4ZbqFDs": "V", - "BVLf4EqukZUKbnXkaDJgx7J15QfxCKkNtti7PksWsCQr": "VFI", - "3YHnXHivm9DiiUtbHNr49mkBctBFy4ksX3PAa3AApump": "VAULT", - "ASBwUuPEGRRgE6ZUChJvJ9zrkeQCmqqvbhc2gGyspump": "VEILRA", - "BWdwwcAJarHt9JtgSgPgFJG1eEcd3vSn9j9DnQKqpump": "VCASH", - "3xhezws6Lk7cMqoVEfZFXu3ry9GKymFz1vbWyQ4f99uX": "$VENKO", - "FRpTyMBDavKsdYN1FQZcEeZ2iwGvHaZKFSkvr5izpump": "$VENTI", - "4JFcUJ1HfFKz2F8thdu2frk558EjrLvKEmeMq1ZM2xBP": "VNTR", - "5SXx7DqZAwnYwRxhBdkcV78RdJrzvCJjRxLmoXdbpump": "VERA", - "igu1coP6n3GPaWmbd8J9Z7UAyLpV254uQFFNfydondo": "VZON", - "DL7JgzgcJLudqJAVTXWyNizT9p9Rk5kxT4bWKUwcpump": "VER", - "AiqBwm3CpoCM6rmsR9BwM6zNXns4YVwjkvpXKNdCmoon": "VDNA", - "vRseBFqTy9QLmmo5qGiwo74AVpdqqMTnxPqWoWMpump": "VERSE", - "FL7QzUq58pvkDxkftJm7RqRWgqYEFZwXuvAMsUnondo": "VRTXON", - "MkN2TZSYTFBdMRLf9EVcfhstTwnazH8knd9hpepondo": "VRTON", - "cPZp4Sk44ecqkwVtED3YPPudQMEe5MbZsavDiwBntAU": "VETS", - "8yeMPJGVVJKRyXdsRBne4ncJekcC5Bd8MtTmJ4j7pump": "VETTR", - "4TQzKV6v18aSVJqrCoxhSuQpFbH7wcQGMi1Wwg8dpump": "$VIBE", - "HJ88bA3HJKgKfDUWEoTqTvvhMng5E6RSAMjwTetCvibe": "VIBE", - "8tSeXYSXzFvaDX1nbWrEFFaFuPHuWqpzksKYxQPvpump": "VIBE", - "DFVeSFxNohR5CVuReaXSz6rGuJ62LsKhxFpWsDbbjups": "VIBE", - "7PzU4m13VqkdgWcserfBdxX4cAPQXaZuFNuKarnT1deV": "VIBECODER", - "7DwehymUxUZgjBtPybVcDNw2QhDr7tNZxuVBEAVpump": "VIBECOIN", - "5r6Acw8ms9VkFeaxrnFE9sxbh5AA2CG1k4edZmpcpump": "VIBECOIN", - "Ed1yisBEAo8UXToSswvVFgzyJKpF48HEdq5kvz2zpump": "VIBES", - "3nf8LgahHm57gEUx64cDNJf4h53C2MwqZCK6LGDTsend": "VIBEY", - "VP9UaBXLM4KYRvyjisu65rz8BU5xNAbewU7LVmyU2x4": "VCAT", - "AZbem4s8iLJE5eniDZJ7c8q1ahbfMwWgCA8TxVW2tDUB": "VIBECOIN", - "7JXwBQDSPQZH4gUy1RGY4Tm5atf8g9v1eNG3h5trrB7X": "VICKY", - "Cx6agZZs3sBnLmyADSq5ecXtxR7cXLPJSsdMv2PApump": "VICTOR", - "2xyoTZzNVvbWABQmaDwYb1mhuvutQkGuCPTSMqicpump": "W", - "HTJKotaFtAmc66UuWHqxnr6Z3tdMWWEn5mP6X69SLKYB": "VICPAY", - "YJWeeW5JBAQJuBrPLZxb42UeFMAAGtfC4NDvNRPbonk": "BUNDLE", - "6AJcP7wuLwmRYLBNbi825wgguaPsWzPBEHcHndpRpump": "VINE", - "F3V1fKLKv7H8aNdt9TC6GQ3X4LayEfGHsPi8Umaondo": "VFSON", - "8fPMSwpxVMR4xLStqAXn9QZwVCCionjDMfqKSqcfomo": "VIOLET", - "BhbfgSh5P742DE5eMx24iZXNZeD2vNRFBZe3EP9Mpump": "VIORA", - "HW7D5MyYG4Dz2C98axfjVBeLWpsEnofrqy6ZUwqwpump": "VIRAL", - "Xsgrm4D6VBTfDx5bs7GNdjtmWDgZ8V79r3YpsYwf5py": "SPCEX", - "Ez4bst5qu5uqX3AntYWUdafw9XvtFeJ3gugytKKbSJso": "VIRGO", - "3iQL8BFS2vE7mww4ehAqQHAsbmRNCrPxizWAT2Zfyr9y": "VIRTUAL", - "28h1NHtVWNS5xbSKZXAsmHr6d8CrqW69BfDm7VQDXSxN": "VIRUS", - "DdcvRiEs4k2fXbHqeJBS5jgGD93rj1wu6u8WTL4vmBLV": "VIRUS", - "7NwukWynkZap56CRwPjxde2HY7z4nFNzzVYTQ96Lpump": "VIRUS", - "kxEW4oJL75K37VeXaZF1ynbHQATQwhECQKN1374ondo": "VON", - "XsqgsbXwWogGJsNcVZ3TyVouy2MbTkfCFhCGGGcQZ2p": "VX", - "YBnTi7GSU2E8vwcoqVcKCRurFafbSRcNfG3kPFRWQuv": "VISIONAIRE", - "h6MW8GFpfzxFa1JNn6hZNnBF3t4fj9SHAXKy6LXondo": "VSTON", - "Xs936JPaWKEXmpu54DZ149GETV7PmsnTqnMWxgcPNAj": "VSTX", - "FpVBzhuQhY3uT1ijxwHRob4NjXQzbcpg1FsgNNTwwBLV": "FLOWAI", - "vita3LfgKErA37DWA7W8RBks3c7Rym2hPFgizNRshBi": "VITA", - "HaCUz8NfR9LqWVQvXqYxR2ast9BHsEScdgS941LKeBLV": "VIVA", - "9RUup1LmD5PBsnd23JmTy39wSwALB7JF7xMfUJP8K7je": "VIVI", - "Ee4ooSk6GMC34T1Gbh8rRY2XLyuk2FsyiWtq3jrHUcPR": "VNX", - "5H4voZhzySsVvwVYDAKku8MZGuYBC7cXaBKDPW4YHWW1": "VGBP", - "C4Kkr9NZU3VbyedcgutU6LKmi6MKz81sx6gRmk5pX519": "VEUR", - "9TPL8droGJ7jThsq4momaoz6uhTcvX2SeMqipoPmNa8R": "VNXAU", - "AhhdRu5YZdjVkKR3wbnUDaymVQL2ucjMQ63sZ3LFHsch": "VCHF", - "5a6LTLwdMJBY2JDLjw8B1w4JwFRdZszGF9W56ZzrriS1": "VDR", - "8c8gJSmaU4yiDsZFWTiCtsMoVBuqp5WGyzSv4VVpump": "VAR", - "9wAKgC8zXwSGaNgtkoNeFFkAfuBkW4BfUUuPkrx6nrQQ": "WTAO", - "9hSaLB4a674pns3uJCYRu4u5Bbvi4UwBcrS9AKnFpump": "VOID", - "2kj1MLqBPnhCTyjxT3MtJhcktJHxgR7s4EDu18khsvgj": "ETHEREUM", - "FRsV3m924aGpLMuEekoo3JkkMt1oopaM4JY9ki5YLXrp": "XVM", - "9VfYm416nEB6iuP8h4Cb9xks7tcdvbwknuMCGEBXpump": "VFS", - "E5ZVeBMazQAYq4UEiSNRLxfMeRds9SKL31yPan7j5GJK": "LDZ", - "H6dZStmJBb5c6YAsnV5JxTbgwJiLjT5zKc4GeBwcpump": "VOOVO", - "vooz4rKUS7PJ2a1r1T3q81E7b5NpGYjsT5YxciCJ4rF": "VOOZ", - "9ruvAhSYeRXqqAz1ffWyKzvPk4SJdLgtMWLXfz5Ppump": "VOX", - "CvwWh9NVQJ12KJ3xqe5SzWAvdohdfD8sePo8STQApump": "VOYAGE", - "5HpFsGEkcyChSovAmKVxtXb4JqVVkBpPnmYPoSgZmoon": "VR1", - "AumaQ5bXzpd5xjsR6snS34FbRGVNNtCF2z2QgVEmpump": "W", - "C4jMVM797K2FqzbwxJn4TdPyuVhso7kCUu7UXRfjpump": "WAA", - "8doS8nzmgVZEaACxALkbK5fZtw4UuoRp4Yt8NEaXfDMb": "WAFFLES", - "3MLpX3VviXDhzZzQwFMHXSRSdXvWWsycLEt3xcTapump": "WAGER", - "5tN42n9vMi6ubp67Uy4NnmM5DMZYN8aS8GeB3bEDHr6E": "WAG", - "Hb4m5r21uoA1rWiNFBJgWN8W52QYbw6UV9jAQDbGNsqH": "WAGMI", - "GnM6XZ7DN9KSPW2ZVMNqCggsxjnxHMGb2t4kiWrUpump": "WAGMI", - "3gqBzYggchmzxCBq5v4BGT4TfmZcm8agsaRqv8bkpump": "$WAIFU", - "GZT8cXcv2uCvFZFi8gGiryXrTXvLZ9RYkPjRuREypump": "WAIN", - "9n8b1EXLCA8Z22mf7pjLKVNzuQgGbyPfLrmFQvEcHeSU": "FLOCKA", - "EcQCUYv57C4V6RoPxkVUiDwtX1SP8y8FP5AEToYL8Az": "WLKN", - "CXyvYhWSpqEbuTXfhfvDaKfHVHaeA9zU4r11cogdpump": "$WB403", - "7zBbQAPGgoKvqcK74Yua8qGwEkEjAZxUPb5m3kKvvHyF": "WSB", - "AFy7FjjKWhQjyebp1dWoJwKUxiPwop9Y7LDFBcPYpump": "WALLY", - "Fo6tfAkXj74X6j8hati8SxtwZHHcdGeqXVUPLP9Abvqu": "WALLY", - "LZddqAqKqJW9oMZSjTxCUmbmzBRQtv9gMkD9hZ3ondo": "WMTON", - "Xs151QeqTCiuKtinzfRATnUESM2xTU6V9Wy8Vy538ci": "WMTX", - "FV56CmR7fhEyPkymKfmviKV48uPo51ti9kAxssQqTDLu": "WALTER", - "2uZzU9nxXjyy8BR5Mcdah8hTezh7ftqgpXx8agAqdCPo": "WAND", - "Bz7vVzQhm2KMW1XgcrDruYega1MiwrAs1DQysrx4tFkp": "WAP", - "8opvqaWysX1oYbXuTL8PHaoaTiXD69VFYAX4smPebonk": "WAR", - "C9dJfTGUzhuqPWxh9mcDr66JZN4uzXh2gh9EWnEdpump": "WAR", - "D26ztC28zZf1QBhrY47NtvK7aeLfDvj41H5wgkUqpump": "WARDOGS", - "XsZUSqxAXKJkEimvD4CoVvEb4WUC92TFgj5zRtBxFeL": "WBDX", - "3fP61JWqLxxrvBv5NSxJdAz4GtCB7UpfU3UFxZTgj1yG": "WAR", - "FPvKvWzSzDZqgYmSZUetrkpUXSwo2VtpR4BynVYondo": "WMON", - "BEaQ6StD98khBPqKjT4mLasqoDa6RuBCXcK4aNMFpump": "WATER", - "WATERR4RJzjvDGZjuMhiy155f6uA9qbbjNFfRPvAKfP": "WATER", - "D6qB6husvNXx6vbHDaCf1xRRxeEya4j5XQ5ojravpump": "WATER", - "B6h248NJkAcBAkaCnji889a26tCiGXGN8cxhEJ4dX391": "WATER", - "8nZZoqBYy13LcMiso4ZdNLRK8BpymJAbyfVdZ1RcEBAq": "WAT", - "Gzb9XduScnJqZ3GKbazbPLWx8Nw8V6uM9VqEZLe8mBLV": "WMDR", - "B2oEUCmCGktPXPkQEzFTPYjnWpUPCotqzaT2eoPopump": "IF", - "5XUUN5z1yBPEwoLztu4qMenmFe6GQnZptBFsnFnEpump": "WAVE", - "4AGxpKxYnw7g1ofvYDs5Jq2a1ek5kB9jS2NTUaippump": "WAVE", - "8Sk2EJ9oo25b7Mmf4qd5gJw6z3738AXvAbkuSSpQpump": "WAWA", - "yJcC48AWnaFQxb4CfZY6U19aQr3Pw6RKVhuGCLVpump": "WOTF", - "FyyDnWQUAcGoJ4L7ab9P7VUFmT3Bs2ggW2DSJz5cpump": "W COIN", - "wxmJYe17a2oGJZJ1wDe6ZyRKUKmrLj2pJsavEdTVhPP": "WXM", - "21nnfR4TkbZNLwvRrqEseAbz7P3kxKjaV7KuboLJpump": "WEEDCOIN", - "E2gLkTXSbbTMmJM19xkquawun2ShJSi7G59A8c2PtbFa": "WEED", - "vyygC9ojzrPKWbHZMCVg6xNiNTHAnXGmAgARfeopump": "WEEPI", - "9pWPUXoZKWNPWyaegPQeR3Kn8aFz9nrGtm5jeAFzpump": "WMM", - "AHE8yDYQJTVqmAYwgMoBRq5YzL1efaXhaXrUeMmX1tED": "WELF", - "L6ZE5qCpVVSqLePz64CrwkgyWoPF9M7tB8BeFH4ondo": "WFCON", - "CfgmB9iTWABdYh3CHQQDUGtWy18rMNAEEqyp4GrJpump": "ASS", - "Dp4fXozKtwgK1cL5KQeeNbuAgFpJtY3FbAvL8JrWpump": "BOOBS", - "8tBUx7LPnA2utkURbyWX85hUus3JhYU14svS3YfGpump": "LEGS", - "9W8ZfTMtQS7iXxiMhMyLN4oqciv92py6td2mTixLpump": "PUSSY", - "23CTZMjEYNNZUE4itfn3iv6kgM4xn7X7dx1kVX1Gr8Xi": "TITS", - "3J5QaP1zJN9yXE7jr5XJa3Lq2TyGHSHu2wssK7N1Aw4p": "$WEN", - "WENWENvqqNya429ubCdR81ZmD69brwQaaBYY6p3LCpk": "WEN", - "Xs4uNhvBDAcp2mz3g9XR5q3vzLgmF1ANWxJgWk2d5u3": "WENX", - "LABSsPBFwfFTW3x584dB6pSen7RWVpx6URP8wfAVdMt": "LABS", - "aQqFBwSUpNKLuAE1ovbrPKQgZJKB1wywBpuiBoJ8GFM": "$SOON", - "WHYoaBumcmxCqw38y2mjs4cVkCBgwiDizbMVvcejmGT": "WHY", - "FLqH2jB2DZPJP5nnVFAakRKaNTcDZtq71Pnpp6Aondo": "WDCON", - "3MKZDaqgMgZJmetXStD47W6FqrcAsK9UaYgNWK2apump": "WHALEFLOKI", - "Fr7vfUAWgmg84L3VuHKiyzTJx3x3V6xU5pn9328Wpump": "WHALENTINE", - "GTH3wG3NErjwcf7VGCoXEXkgXSHvYhx5gtATeeM5JAS1": "WHALES", - "EaxDJbZ6L7FdgQMnHRwqTrasJXQRfuHY5oDtNG1vpump": "WTF", - "3e2ENpKfvrrAfYtCRUzetJWQbJy79fAteaPehqjupump": "$WALLAH", - "s6nhgeppKdbfockwnrUAA5art6ca4Sa4enN5fNApump": "WHATEVER", - "C4u9GYmTvtaGa1a7q6iijn5DK2GYe78fqEeoPwrpump": "WAPE", - "9fvZcQkCdRwm74uSVf46sfMHiYHtSta8ZdMCaLYFpump": "IF", - "Adq3wnAvtaXBNfy63xGV1YNkDiPKadDT469xF9uZPrqE": "WIT", - "CZJzsywCWkqZNa6CRW3Av6WVNCvTX3EuBwS2nar8bonk": "WHATRR", - "84yCF7hyUuRNAra9BjFHVDpnRdGXmfsV2emyZwLjpump": "HOUSE", - "HJ39rRZ6ys22KdB3USxDgNsL7RKiQmsC3yL8AS3Suuku": "UPDOG", - "DHAajf71VVT9s98hmyFyr3JDnr9KfotFbM5gQJV5bonk": "LORE", - "6iA73gWCKkLWKbVr8rgibV57MMRxzsaqS9cWpgKBpump": "我的刀盾", - "4Q8zRhEUH8oTJvfd1qXxE2m7zWSVFjHHD7sZPvWWpump": "WHENHE", - "5XCZVKbQhDQpyP1kc12UsyqarN7d4gFZyTdk2Jsdpump": "WHINECOIN", - "ATeTQcUkWGs7AZ15mCiFUWCW9EUL7KpDZEHCN1Y8pump": "WHINE", - "9UNqoPEXXxEnEphmyYsZYdL5dnmAUtdiKRUchpnUF5Ph": "WHISKEY", - "whispF7G9DHaojYHe2cdhRX5EMJzGBdqq7R57kL6inL": "WHISP", - "EEBA6E69rhvGgLSs633u5qVeNjo77fp2wY3yj8Aipump": "WHISPY", - "6Hb2xgEhyN9iVVH3cgSxYjfN774ExzgiCftwiWdjpump": "WHISTLE", - "GJgHsc1HU4ibmzW6oWQr8L2RRT95ATc1BoNuLkp94AwU": "WBS", - "BykkD9369RvXuEDbR7pTRz49b7cfLRTzHgSVoqK8gc15": "WCC", - "86WM5NBUtRWTHULKrspS1TdzVFAcZ9buXsGRAiFDpump": "ICEBEAR", - "ENWC66tsY6cyqSrd8a7S9ETc712xCcSAuGcRUwSMWhyo": "WHITE", - "HhUVkZ1qz8vfMqZDemLyxBFxrHFKVSYAk7a6227Lpump": "LI", - "5qmL9rCSfZ7pBYAsaoeG8SP76ZELeRCK8XtMmYZvpump": "WIBWOB", - "WFcgBXhGxyFDp43AgT9opi3gyJLtfEo5bodLKuQpump": "WIDI", - "CceCXFy4UeXheTpf8sn1bpECxMQeH5BTyxQK4BdRMf2T": "WIENER", - "4y3oUrsJfSp431R3wJrWiaLxRPsnYtpkVJmoV2bYpBiy": "WIFE", - "CoRQpMjhBx6AsrzKJJZyzJoH52GoEKShM3CVumyzpump": "WIFMAS", - "21AErpiB8uSb94oQKRcwuHqyHF93njAxBSbdUrpupump": "WIF", - "DBh9F2wSukCJdZeNkLYe2LMYnCwMyLcnBgeCf4pgpump": "PIGGER", - "FVvd3s9dZYzsgitkJyWbmycSc8MkYZjyF7oqAEvmSxTZ": "WILD", - "8aB8hdzyqjCJdtP1rfdLNX8bGDjjM8S42BZ3j4zNVtRN": "WGC", - "3rSKhRtrjHnSZhRsgoAtrWRVM8rcf8DYv8Quea1epump": "WILL", - "4RYtJLcaGKMkYbodJ7X8QYPd2SnRWRvSVWMxfzwRpump": "DOLLARCOIN", - "3bzeQJqDPfQQbxJ6VmtMkXyyQEJrj9HS1SMvHR1Hpump": "WILSON", - "CsxCtA8usvWKdRhe7KhLU5GgwzYaLkoHsz1MKBVZ4W3M": "WINR", - "HSpwt7Z4Hb3139FWZR8W9q17yWpxffm47a3g63bQpump": "WINSTON", - "4j2gUEmfbSAacvSSd6yXo8yEzXCAUVeoXrqLVV3apump": "WINTER", - "2GXmB95pGD3pHV4mzNq7YsmyiEmVYWQLptbwYqgdpump": "WNTV", - "o6U1Sm6Vd7EofMyCrL28mrp2QLzgYGgjveHiEQ5ondo": "USFRON", - "gnoSQSNTNZHViqVfxCcPDVxcRA29mrJL7C6JqYLondo": "DGRWON", - "9z4EGSy2KBvUX3zb4pJSWdoFKs6nX4SNh32JepBTpump": "WISE", - "3gJZBpRUPZnQ9xbLDwuEJ4GC2576Y2dYriJDsUJpmoon": "WISH", - "2TobzM4NNpEvyVAEpFC8AkxJGBHz8fFgMksK1gBy5dub": "WIZ", - "8oosbx7jJrZxm5m4ThKhBpvwwG4QpoAe6i4GiG19pump": "WIZARD", - "2AXfPwXtqy8KVwWLcHNNHbh9KejZhDb5WhKnoH4Cpump": "WOB", - "9yZ5Ru8pbmJZ6Q2DKLCGXkaLNwkm83cnJ4QCw4PFpump": "WOBBLES", - "5kLu7okC3yrL2NKznwQtkvCVoGGyo3iWz2nht1fXbonk": "WOF", - "6bn6rKWbTRAQwizwqEfcmpAtoAMnqzfG7F8DYMK5reYn": "WOJAK", - "7VYXBSQa79BuBrutcdKMXJodT8TLS7aDv6chaF3sbonk": "WOJAK", - "7oLWGMuGbBm9uwDmffSdxLE98YChFAH1UdY5XpKYLff8": "WOJAK", - "8J69rbLTzWWgUJziFY8jeu5tDwEPBwUz4pKBMr5rpump": "WOJAK", - "wokaXpAAtg6jg9wa5PEvEFY4vuh6Zrcd47vBTL143wE": "WOKA", - "BTr5SwWSKPBrdUzboi2SVr1QvSjmh1caCYUkxsxLpump": "WOLF", - "9ZMEz6nmr4RWs92ASFKxFGfKpWw8cbUp2ZP3EhdqwWPr": "BALLZ", - "BEgBsVSKJSxreiCE1XmWWq8arnwit7xDqQXSWYgay9xP": "WYAC", - "GEKjZKJZgQTCbi9evTW2GmhyamH3sq6Lid9dQMWqEcCY": "WONDER", - "6bunJ76HV9SCDHkukezST3VKzQKJ8JtjTQQ92UD7pump": "WONEY", - "8KvoBfxiPiVp9b9mxSFZHpJdX9NUtbmNFhAGggLDpump": "WOO", - "WARcU61sECeEK5DEbkY3wcjGxSGr4W8bVUpKbBNbgbu": "FINE", - "5UF9Q7tdkGnZy8MoMrYqe6tcAZJbSaNWMGuUnJajmoon": "WOOFI", - "9nEqaUcb16sQ3Tn1psbkWqyhPdLmfHWjKGymREjsAgTE": "WOOF", - "AZpgAvSHUdzDHd7sJsQZgmKkz6ryHAN1fxkGWJ5Ppump": "WOOLFUN", - "47NF9q76FaLAbZQmBaHeeUWYsEn4Rt4qjmh1oACipump": "WOOLLY", - "Dz8VutERqbHR2aFL5A3s1Ky4dG1unJT1jUFXXPaY9ytX": "WOO", - "A3HyGZqe451CBesNqieNPfJ4A9Mu332ui8ni6dobVSLB": "WOOP", - "F7Hwf8ib5DVCoiuyGr618Y3gon429Rnd1r5F9R5upump": "WORK", - "WLFinEv6ypjkczcS83FZqFpgFZYwQXutRbxGe7oC16g": "WLFI", - "WMTXyYKUMTG3VuZA5beXuHVRLpyTwwaoP7h2i8YpuRH": "WMTX", - "6KXjdTtGqXMawUqcAypAYXCdav7rXQYZjDyejfjQpump": "WM", - "atVjZ7uM8sVrLFi5Xe1JiLGW6mW9pvQdTCWzhNFpump": "PEACE", - "JCcd7NmyHVmiuycHP6drEesBSZhZUJXhBMbhxq42RNg3": "WRT", - "DoLdHPVQixK98xmDfnNh3FfF2fSFCikK7DbFDGtwpump": "APPLE", - "CLyhG2aVrnvbxXAXokBGvKjmNfsamPKtGW4Gu9AHpump": "WSOD", - "HtrmuNs4nESVg5i8gHyufDpGN5HHKqPmFfbQV9yupump": "LOLCOIN", - "865GVDdESzXiNvygSUt7yFeJLrXDyn5UpGNfYuxfpump": "HENRY", - "85VBFQZC9TZkfaptBWjvUw7YbZjy52A6mjtPGjstQAmQ": "W", - "98sMhvDwXj1RQi5c5Mndm3vPe9cBqPrbLaufMXFNMh5g": "HYPE", - "AdsT8339RRmJbYCJo6d75cAbTguC7yMk4pft4e19pump": "WORTHLESS", - "GEo9uMBTvagNAX6Paf4KEuZ94Wi5K1ZjYfvhoSfKpump": "WORTHLESS", - "iUdvUaxyRHh8PYVcmkgBpSJu5evpW6jsSLv8RCpmoon": "WORTHLESS", - "9pgfoXFAN6oT9dNswE23oj2kCkCpoLPzHVpTKGAtmoon": "WORTHLESS", - "J1Wpmugrooj1yMyQKrdZ2vwRXG5rhfx3vTnYE39gpump": "WOULD", - "F87d3uwBN7epMt89bUaFdoQmw24Eggo1jjmH5Zpnpump": "META", - "xALGoH1zUfRmpCriy94qbfoMXHtK6NDnMKzT4Xdvgms": "XALGO", - "C1MHyoTJpRTeS9AQCyspNVu2EWAYCZwmJ1jNkEArFP1f": "WAPE", - "5XZw2LKTyrfvfiskJ78AMpackRjPcyCif1WhUsPDuVqQ": "WBTC", - "9n4nbM75f5Ui33ZbPYXn59EwSgE8CGsHtAeTH5YFeJ9E": "SOBTC", - "21BTCo9hWHjGYYUQQLqjLgDBxjcn8vDt4Zic7TB3UbNE": "21BTC", - "3NZ9JMVBmGAqocybic2c7LQCJScmgsAZ6vQqTDzcqmJh": "WBTC", - "GYKmdfcUmZVrqfcH1g579BGjuzSRijj3LBuwv79rpump": "WDOG", - "2FPyTwcZLUg1MDrwsyoP4D6s1tM7hAkHYRjkNb5w6Pxk": "SOETH", - "WFRGJnQt5pK8Dv4cDAbrSsgPcmboysrmX3RYhmRRyTR": "WFRAGJTO", - "WFRGSWjaz8tbAxsJitmbfRuFV2mSNwy7BMWcCwaA28U": "WFRAGSOL", - "zZbQjiRg8uSxZaPu996XuviuZeSY6nsaMuutKZQBJga": "WFRAX", - "mzeroXDoBpRVhnEXBra27qzAMdxgpWVY3DzQW7xMVJp": "WM", - "CrAr4RRJMBVwRsZtT62pEhfA9H5utymC2mVx8e7FreP2": "WMON", - "HcFYJVeRGNR5YJjJVvjtpcA6kXEE2v7QqY1pwymG7QRL": "WPAX", - "2GBVt2ENvbHepuJMWYTPkkfpWUabAhsaXToYw8UphxS3": "WRBNT", - "So11111111111111111111111111111111111111112": "SOL", - "3rQK45d1ojXR7vtvCmeNjKKVycnVWqaVcP3zk1G39RJR": "WS", - "CXLBjMMcwkc17GfJtBos6rQCo1ypeH6eDbB82Kby4MRm": "USTC", - "9hX59xHHnaZXLU6quvm5uGY2iDiT3jczaReHy6A6TYKw": "ZENBTC", - "JDt9rRGaieF6aN1cJkXFeUmsy7ZE4yY3CZb8tVMXVroS": "ZENZEC", - "5B8jpiDY6WftBdwEyL3LWwYKeDSVikUpXoSYZhFxbonk": "WRONG", - "8C4RygkxmePm9ys1qCcAB46dUCXNQYTaqfxS5mBrpump": "WTFO", - "sdFmUe2Q8Ho8SEhbp5uXU9d4wCZzD8fHSvhLC6mX777": "WUAI", - "DaeRKF1nBMog8CTWp5SBwkGaPed4RzHaofoCi4rboop": "WUFF", - "73xsLcBnLnc9bh81cqVKqj8uEyiarXng5ZwJuTbnVebG": "WUF", - "9b2FQBXgUZTpZk4PNfJa2sNPK3qf6kS4PxnQQmWpoa5E": "WUKONG", - "ALR5X2H6THn2VDPoMtkVwxVktcN1kQGvxCwLfejzpump": "WURK", - "DG5bH1BnfjB5YL7Vt3GjykkUKf6maDUW3jYvdNa9eEVa": "WUT", - "2yoTWgHH8KjyXKi2zrRZmA6AbZGa9zztnE8EFCboxWTe": "X1000", - "DEffWzJyaFRNyA4ogUox631hfHuv3KLeCcpBh2ipBAGS": "X1XHLOL", - "6cNcXWqYvK9nhD1TsjJ1ZH1KATXcaPaRJtZPHyVkJoBs": "JOBS", - "3E7LWnvBjKiauBJfbSFe86A1i5Rnzzs853bbjTmApump": "WALL", - "6ikxp2KnQcdCik8Aadi2ScE7qgH4j5C7BvSyA29Wpump": "X4PAY", - "KfByHk48ecitUq8gXji2vr9smmRJKtqJwGAh2E9pump": "XAIC", - "PreC1KtJ1sBPPqaeeqL6Qb15GTLCYVvyYEwxhdfTwfx": "XAI", - "XANDuUoVoUqniKkpcKhrxmvYJybpJvUxJLr21Gaj3Hx": "XAND", - "XAnDeUmMcqFyCdef9jzpNgtZPjTj3xUMj9eXKn2reFN": "XANDSOL", - "69G8CpUVZAxbPMiEBrfCCCH445NwFxH6PzVL693Xpump": "XAVIER", - "3wppuwUMAGgxnX75Aqr4W91xYWaN6RjxjCUFiPZUpump": "BANGERS", - "XBGdqJ9P175hCC1LangCEyXWNeCPHaKWA17tymz2PrY": "XBG", - "A8YHuvQBMAxXoZAZE72FyC8B7jKHo8RJyByXRRffpump": "XBT", - "AHcxvCP5se2DiqfcXwBz8i7WygwWJdd8f9S9t6nd4YsQ": "XCAD", - "9HAZrXJx5QXzBfJynRQXkHr9DG4Kk8dzuuQnMfz6pump": "XCHANGE", - "BmtmfGfvF23sqKh5YCAVxgf9AWYpJc5zqyySxHi8SLom": "X", - "EhkDgP8e5sz16AFCWUCYJzVug1zvpDLTEHxH4WAFpBFA": "X", - "9Enb2cGtfYyAjCQedPUYd6Hfq2gkqzKH114KkDRZpump": "XD", - "ERtzyCSu9FPvdxwsUS13cueHfFWQNkSvbKh5nTpUpump": "XEND", - "J5LugyRpwHi4TYB16UYWpDuSx6z5zsuJ7pMjU95Cpump": "XENO", - "Db7ZUaWTThwZy7bVhjn5Dda8D3fbbAhihcxPV4m9pump": "XENO", - "9ezFthWrDUpSSeMdpLW6SDD9TJigHdc4AuQ5QN5bpump": "XERIS", - "D1zY7HRVE4cz2TctSrckwBKnUzhCkitUekgTf6bhXsTG": "XEXAI", - "HP6ypiCX4yjzSxpfJExEcydSU1BiuuvGhgrKCLKopump": "FRACTAL", - "3rXPMXUHpRCYpYfdZjL3ut2fNtpW94Zik4GH31eRpump": "XGR", - "5gs8nf4wojB5EXgDUWNLwXpknzgV2YWDhveAeBZpVLbp": "XTAG", - "GnaXkbmMV1zGK6bRCQnM9Jd6Jv2Hjw5b2PFVBaKEE5At": "XIAN", - "EDavhezsuNnhdoAKPExWaMtnuhq6FVqoBYnyFEJLLBqC": "XIAO", - "5JcdnWEwuHh1v3SAARq8zH9tEwDQGpaHzBrZ81m4pump": "XING", - "7qDrjF1zkDJA6LombCda3duwC1ABbAYancTiRHZwbonk": "XING", - "7NoFtDpmQvE9JsRv7o6zgYZJBEcixWumJaZsDoyrpump": "XINGXING", - "7JoGUTeaXkjcMGa6xJP2idYNYiLreto1WnsACHnpw3Gd": "DEUS", - "9BYVxhYF4tWSRVyJUBSVC8PaAhuuiCiAbmfy4hUApump": "XMAS", - "5csfa95Xf8ebiCwP9joQ7mtC8KwFvnnejnYx5FbYpump": "XMONEY", - "xNETbUB7cRb3AAu2pNG2pUwQcJ2BHcktfvSB8x1Pq6L": "XNET", - "xoUSDq85Rjsb6SbUwJyreFgeWQvxdkT7R3c3g7s6p5Y": "XO", - "HEPvyunjSoiua8rAWZVMjgZ5TLp9vMQ61iGQh2u7pump": "XOGE", - "Ckit5s1Cpc3RdMh1HrhfW2nAy4PnkkgjXgXMeykbpump": "XONA", - "677jK6xujhRFtBcD7Szn32tt2MSv4k6qZ4v3nGMgpump": "XP", - "BoMbSn3KcWsUe1dgz5ddJrRaM6v44fpeARNA9t7Dpump": "XPI", - "DmxDBXTxEnNXVNNVVZe9QhgoKnr8anjE1b49dpZapump": "XPT", - "AY1ZUqnLhrBwtrsKb2EJY4Hthaq9aaorifhFkQQPory": "XSAI", - "2vfBxPmHSW2YijUcFCRoMMkAWP9fp8FGWnKxVvJnpump": "XTRADE", - "CYDZoP9MvtJqyorDCpEh53pqQJ5FCe8RTjE2NK1wpump": "XGO", - "7NaZ55Pt7Ah3xPsmWRfdEi2mv3dsd9TpHSWhTEt6pump": "YTC", - "8fLJCxZiMDd4e5wsXAGWEDdX7q2UgdW7EKeQU7jypump": "YADOM", - "GdZEGK9Dv61EbpJ8fYnJZcnJvmhwF6uS6mVVkSfSbonk": "YAGI", - "5dDTxqg38NUY6ZKKGXXjaopWHt2xc3zSPoKhKmEEpump": "YAIT", - "AqEHVh8J2nXH9saV2ciZyYwPpqWFRfD2ffcq5Z8xxqm5": "YAKU", - "7otQqMyZrVLfVg3nf6BdxZLDL52q2A1e97H3GCi1daos": "VYBE", - "3gcdoCBQMKAFVopsQ377X7JhkcNPg9fwPY4eW46Apump": "YAP", - "H1aoUqmp2vJu5o8w3o8LjrN6jKyWErS69PtYxGhfoXxf": "YAPPER", - "8CzPPqenpfRXvjE5DHfE3kMDgJhHwWZ1WXRzs44KsamW": "YAPSTER", - "EgHo1Tq81VHKSYSx9KiWEsZvKRb5fZD9M4y5HPyUmoon": "YARA", - "7eLSZuUZ66YA1JPYmpG9ApgoTdZmzHDfz32YngbPpump": "JUAN", - "HK6hRLHB9orDKK5MffpCKxdLLRqiaqqUxN2vEoQQpump": "YEE", - "3JVxH5QT9z3v7Gi2z4K4N69Bma6PHj8uovX8tYdQpump": "YELPE", - "6r6YZkHNmphgZYYrhMzrXSXqa9i8MJhQigM1jCetVvbQ": "YU", - "6brGsbvP1PTitDGGsz1TXF2CvvvRkS5QvQA8UH3ooe7i": "YES", - "FuyeX8cpctBwQVDFYgKxYh1JgiXCkK9g4RBVCXm4pump": "YETI", - "DR9fWuj3XRFyNzxk1rvkwMMYA2WHj448HgVJduQLpump": "YETI", - "3VcKofugG1SPJmjuiEZCJL5mk1JkyqGZ19ByeMWXVWfK": "YBTC.B", - "8ougyS81NXMKVTRvf8wdxD6DhbtJ5hcDRXhuE27ApYLT": "YLT", - "8SNKhDsVUhnwuRzGAKsK4E2SYTcoJzocPKQyoBryjwVz": "YLD", - "7D1iYWfhw2cr9yBZBFE6nZaaSUvXHqG5FizFFEZwpump": "YNE", - "EjpYgSJ7FHWjyTNeuDvA4rfdpCP5cnbmfnqNNyyspump": "CHAT", - "BNSPDd1Ws7AqiaEMZWVA5gcuAYGiLFZYAzXz11BGpump": "YOD", - "nDrv1j5tkhmBEJTAmEkEn19kjw54d5GXN8e68HLpump": "YOINK", - "5e898RCuzJkv7J6N3hNxK5pK6xZWM6RzZDjS8Esdpump": "YOINK", - "E2BLSv4tXo7qA4Pr3nkJqxyn6743peC7x8E9D5s8pump": "YOMAMA", - "GF8ysB8rF9WjhrK5uVhRReubJtWpPzbztB7gVfAgsurg": "YONA", - "ENTAzGHF1YzeK92r6GFFvpbXuWPt5MV3gjckfJUS5i9i": "YPP", - "7SdFACfxmg2eetZEhEYZhsNMVAu84USVtfJ64jFDCg9Y": "YOTO", - "BXtTxGMRygG7DxufDFVaGP1WdvxSscWN8VoQ2zLhpump": "HAPPINESS", - "QpRvqVMPG4o5y5fARHR6L7aK1x5vdWPQEiEjMkVpump": "POV", - "F7pB3ZdfBnyFw2LRHydWEn9BmhEa5XihXLjhySFRpump": "NOTHING", - "EkHr62PC6Y1axrLS7cR8YC4BZeW19mtHxQLCLMrf9vnq": "CIRCLE", - "ANAuiz2JjRvNtyW8cd7UsQ4LRWB1PTGhyrgMWPxtpump": "YOUNES", - "FjK6rqU6QzUeNtmK6QQ78cCuS5AHAhcm4HgJkdsvXaep": "YOURAI", - "BAAF8R9EBNAo2aCPQzYiP6hEx3TXKiNads2MrdDjpump": "SLOP", - "By5XddQNE4SB2XbgvreFeMNHL5zqgGqypiN3Xor3XmvM": "YOURMOM", - "7mM2D6brqpKSZ8ttuEehf1XCukQMJSHutQXBQxtypump": "SAVINGS", - "GnDfrXWPs71J73CrfrNsuwPrR15Fu7oZfNxYRhHWpump": "OUTIE", - "Etd4QU7PGuzh4ozkzzBBjMmySNkU21BZamB7qPR1xBLV": "YOURSELF", - "66gsTs88mXJ5L4AtJnWqFW6H2L5YQDRy4W41y6zbpump": "YOUSIM", - "qfAV15JCvquMYpjGGkhQGr8x2zTiw14pCpUCjXLpump": "YOWIE", - "Gtj3QCZgZNbqzpqQVKzpakgAbTEwPt6LdiLjWYNZpump": "YSKA", - "YUYAiJo8KVbnc6Fb6h3MnH2VGND4uGWDH4iLnw7DLEu": "YU", - "6XRFXWBrLmswPsJtiEdMataCKJfeD3vVVk7HygSpbonk": "YUCKY", - "9VBhKuR9DxDP1wQCi9MPFY5q8fkYb62CPUYpx4Gppump": "$YUMI", - "7fRQixgMhULQxNQzaJrFEZyjmrBgJi85up9gZrcupump": "ALIVE", - "4cp9JPLZBEAvw6KQbfxnQEEVHVd7wne1gD8FEZKupump": "YUPFUN", - "AbBADaxdQ1LtrU7sN9xvf4326ECSYbsUB37ddbRrpump": "YUREI", - "mm7g78aMJZv8QB4Q15qbsM3UQJZWof85vwC2NSxRh2Q": "YURU", - "Co22X8zmGHsiYKcK9sRq53MAwdDQcDdV6uvJtSLSpump": "YUTTY", - "DrZ26cKJDksVRWib3DVVsjo9eeXccc7hKhDJviiYEEZY": "YZY", - "8vCAUbxejdtaxn6jnX5uaQTyTZLmXALg9u1bvFCAjtx7": "ZACK", - "DtTPiFFX5oNnozjfoyYfDRFcSdPDuJNkYaZeEvSCBAGS": "ZACK ON BAG", - "GorvUUSkJGSzJUebM5W6berUkZ5pG8nmnn7tmk74pump": "ZAILGO", - "EAyvG3YKGsnHkQLcmY3R8YRSab3wu2rC288U8f8PVwND": "ZALPHA", - "4Zp52aF4hZi9fzH19xpbWKYKQvgLyCN67KFbrQDqeTKh": "ZAMA", - "HxPoEHMt1vKeqjKCePcqTj6yYgn6Xqq1fKTY3Pjx4YrX": "ZAP", - "CCz5hPGJobjtVGGrEiUhHAz9qCEt1KyKxFkGBjVcjuvo": "ZAPCAT", - "zapzy5CRnuo8tVP7p4E1Ec6mc7XS43JHiiqv7FLniXa": "ZAP", - "73UdJevxaNKXARgkvPHQGKuv8HCZARszuKW2LTL3pump": "ZARA", - "AbzXS6NfGvCtg5B1rqZ1JSfoDHkwTAeEYJkWkHhCe38W": "ZARO", - "dngKhBQM3BGvsDHKhrLnjvRKfY5Q7gEnYGToj9Lk8rk": "ZARP", - "DNhQZ1CE9qZ2FNrVhsCXwQJ2vZG8ufZkcYakTS5Jpump": "ZAUTH", - "3QJzpi68a3CUVPGVUjYLWziGKCAvbNXmC5VFNy1ypump": "ZAZA", - "n7EksMkvk3WT5FjQ3HBMLm9XYU3EnoXUSJ7PoWpxsoG": "ZAZU", - "qiaupfns561LJPudU2YL48S2mx1nbekrn8V4RrpyJG6": "ZAZU", - "8RSbsKW26WhHsFsM6jc34zSijvq6r7t6GmkYrfj8pump": "ZIP227", - "GVvPZpC6ymCoiHzYJ7CWZ8LhVn9tL2AUpRjSAsLh6jZC": "ZC", - "CCpTDK9dDdmvcQtpPrv6zDBWXgjqq5LrJHidJW7Jpump": "$ZBEC", - "ZBCNpuD7YMXzTHB2fhGkGi78MNsHGLRXUhRewNRm9RU": "ZBCN", - "zebeczgi5fSEtbpfQKVZKCJ3WgYXxjkMUkNNx7fLKAF": "ZBC", - "7unYePWUHcpB28cnS65TpqT2qqmZaftRz9QABkdR8yN7": "ZEBU", - "6r7rgqYrdEXrYrTT3bUdefJE7woHBNsWF7c8gkmFqdev": "ZECFI", - "CA6DpicXuJUsSWziuV4oHmqpHTSMZLz9EGjYPyXJpump": "ZEKTA", - "8vwqxHGz1H4XxyKajr99Yxm65HjYNVtqtCNoM2yWb13e": "ZAI", - "5DkLaee4Ctm9v8bka1zGeuPUTbngZaznZsc1t3L4pump": "ZENAI", - "ZenexEqqyiYCjykxYZr2cW6g3xVwQ5RggHmo245q5J5": "ZNX", - "3pH7FnM3yR2Jy2c6vyXmsCiNi1rjQ7gerCq2pEUHpump": "ZEN", - "9wpLm21ab8ZMVJWH3pHeqgqNJqWos73G8qDRfaEwtray": "CDB", - "Zenko9EfTmbYVUw8RMjEW4vf7bNPSd419EjtpAFXeWw": "ZENKO", - "5VsPJ2EG7jjo3k2LPzQVriENKKQkNUTzujEzuaj4Aisf": "ROCK", - "2a1sAFexKT1i3QpVYkaTfi5ed4auMeZZVFy4mdGJzent": "ZENT", - "By5umkRTaquaW1sgj5aTUwxPFWjJC26VBq982fadZNTR": "ZNTR", - "8avjtjHAHFqp4g2RR9ALAGBpSTqKPZR8nRbzSTwZERA": "ZERA", - "8x5VqbHA8D7NkD52uNuS5nnt3PwA8pLD34ymskeSo2Wn": "ZEREBRO", - "G1r487Exo1Z5pLmkqLbdhT7s18WoRr6pDpdD3uSqpump": "ZCOIN", - "AnGzGiQ6ddzdqWDuWy6vmibqDv8rsT2QKhcgDrwTpump": "GAYCOIN", - "AhDD3J3SydBq1VrXkWsgH8Hb62FWtTqzgn3vafsRpump": "ZEREPY", - "GKEGxPuznVZYHh5J4Hqk8kSabgPjJTYviLLhfF47pump": "ZERESYS", - "FA7BUyYa1kGaLq34YTnMPPaCYcT1r8PWxM5yqjxALEwJ": "ZEREUS", - "AA8nmXa7fwfYjYtKEaKwkcCKJL5UmkvXYXE7NVmspump": "ZERO", - "Cf9hZDWdJsQPrt9q8yrTcyc9PSDmQUyXktHz6rFu8amJ": "ZERO", - "H1hcBegR2A6b2mCGqhH5zA4sTZvMxrqT4v3fHY1kpump": "ZB", - "SVdeWmHnXsSSeU6sE7tf6x8hwBG5jYuUW141pRapump": "ZEROCLAW", - "6kP6Qjgo7Vke9HTe3tZ9DeRtKw2NDiE5fDABWEUspump": "ZEROC", - "BoRiiSZ4hqtbCnQc7cWaEYTEtxmzRXyy5xaKCt7Ppump": "UNTAXED", - "AWc8uws9nh7pYjFQ8FzxavmP8WTUPwmQZAvK2yAPBAGS": "ZHC", - "BwUTq7fS6sfUmHDwAiCQZ3asSiPEapW5zDrsbwtapump": "SOLFUNMEME", - "BAgzJnohH2kjSGkZwkMRWLr2qmYUEdWey6zavkUKpump": "ZEROPAI", - "3n4jw9KSH3UtLeJ4JE1DDA2Y3TCAu8nmkfoznVPiEtjT": "ZEROS", - "ZEXy1pqteRu3n13kdyh4LwPQknkFk3GzmMYMuNadWPo": "ZEX", - "Fc1iGoPhSFz69dSfYSMbKeQjiyQ1FHibPpSkDiuApump": "ZTOCK", - "Fe3sEB1cAt4TsETqmaopPQBto5o8TsY2W2NTw6H9pump": "ZEUS", - "zBTCug3er3tLyffELcvDNrKkCymbPWysGcWihESYfLg": "ZBTC", - "ZEUS1aR7aX8DFFJf5QjWj2ftDDdNTroMNGo8YoQm3Gq": "ZEUS", - "GvRf47WPg9uaYcyXEs5UxHL2D39P7yTByBDrQcyMk5wg": "ZEUZ", - "26f12PmBk77wQV1TzLe8XKkNBvMFggbuypxdtMLzNLzz": "ZIG", - "Zippybh3S5xYYam2nvL6hVJKz1got6ShgV4DyD1XQYF": "ZIPPYSOL", - "4PX31xRA1BaAyb2Js45ZKYp92VGWGp47yWeVs5CGVKbf": "ZDLT", - "8qieHd2Ev6yn3qD2DQENXiNDuYRJ2wHFghNgpsnYpump": "ZK402", - "3QQwbsqNMzy6JjwmoM9FJQ74ei1FmfoYj5jT3EhPpump": "ZKC", - "4K3aMpVPggTWAw1jyN5cZJf3kpWjnfTYmxkVbMymhuwj": "ZKE", - "6pKDTUavWbdHH27nUBeZNGDSmg3gw5XMGq6gAPUf264F": "ZKFG", - "ZKFHiLAfAFMTcDAuCtjNW54VzpERvoe7PBF9mYgmeta": "ZKFG", - "51mxznNWNBHh6iZWwNHBokoaxHYS2Amds1hhLGXkpump": "ZKRUNE", - "9KmJLjfHxxd1LvwmAgvAT5oTFMaY8y15UiuEMZJKpump": "SHINE", - "9Yn6bnF3eKLqocUVMxduh7WWqgQZ8DvWQDYTX9Ncpump": "ZKSL", - "DTanHWf8jRg4cgiMfVYReU53J9Za2mGSm9cS3cahpump": "ZKTR", - "CxpGDVrX2rA6penHD2zhopPYNMXAtXutizWSktwytZ18": "ZALA", - "AwcCFuJgUYNYHXm6tHhr7DsXDY6FKvXUt2DFjmgHpump": "ZOA", - "J1ow1c3ExcJYQmgNFRPfCp1LMYRf6P3A33isDcBmpump": "ZODS", - "EGhdtB7B12ezVooo6CwqxjiFUUCJXbDXx7iE77uSpump": "ZOL", - "HJBoRECiJddTZQZpuY8pHenf5CZ2yjju4npekmvbpump": "JACKJACK", - "nBZEcHSG771mRbi4y2sSgKjfDUH8jsM2Eo5fNcASLeU": "ZOOMER", - "9MBzpyMRkj2r5nTQZMMnxnCm5j1MAAFSYUtbSKjAF3WU": "ZOOMER", - "BGqXaVjjQy4h3qgqgzT9TrzyAGqHmBKhgQwW1VFmeme": "ZOO", - "HHkUs6Sh6mE5LeLonpAX3VFvpR8h8EYzjKHrt52Mpump": "ZOPA", - "GgXVZA9Ax5uW2NG2VMQYmjkjBmwPekTE3BCJr7NXpump": "ZOS", - "GnFMf6JVRhAqPbA9r8yW16xycynKNkXfbaYbusLEpump": "ZREAL", - "PzuaVAUH2tfxGZcbBR6kMxeJsBngnsPLFotGJNCtcsd": "ZYN", - "FefBQzgFmAynFwKAqfnyAXVv3bBXVgA1A2tuXEjbpump": "ZYNX" -} \ No newline at end of file diff --git a/app/i18n.py b/app/i18n.py deleted file mode 100644 index 4afe847..0000000 --- a/app/i18n.py +++ /dev/null @@ -1,44 +0,0 @@ -from __future__ import annotations - -from PySide6.QtCore import QObject, Signal - - -class I18n(QObject): - language_changed = Signal() - - _instance: "I18n | None" = None - - def __new__(cls) -> "I18n": - if cls._instance is None: - cls._instance = super().__new__(cls) - return cls._instance - - def __init__(self) -> None: - if hasattr(self, "_initialized"): - return - super().__init__() - self._initialized = True - self._lang = "en" - self._translations: dict[str, dict[str, str]] = {} - - def load(self, lang: str, data: dict[str, str]) -> None: - self._translations[lang] = data - - def set_language(self, lang: str) -> None: - if lang != self._lang: - self._lang = lang - self.language_changed.emit() - - @property - def language(self) -> str: - return self._lang - - def tr(self, key: str) -> str: - return self._translations.get(self._lang, {}).get(key, key) - - -i18n = I18n() - - -def tr(key: str) -> str: - return i18n.tr(key) diff --git a/app/integrations/__init__.py b/app/integrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/app/integrations/chainlist_client.py b/app/integrations/chainlist_client.py deleted file mode 100644 index 429f9ab..0000000 --- a/app/integrations/chainlist_client.py +++ /dev/null @@ -1,84 +0,0 @@ -# app/integrations/chainlist_client.py -from __future__ import annotations -import logging -import re -import requests - -logger = logging.getLogger(__name__) - -# Источники в порядке приоритета: первый успешный используется -_SOURCES = [ - "https://chainlist.org/rpcs.json", - "https://chainid.network/chains.json", - "https://chainid.network/chains_mini.json", -] - -# Шаблоны RPC которые требуют API-ключей или не являются публичными HTTP -_SKIP_PATTERNS = re.compile( - r"\$\{|wss://|ws://|localhost|127\.0\.0\.1|0\.0\.0\.0", - re.IGNORECASE, -) - - -_HEADERS = { - "User-Agent": ( - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " - "AppleWebKit/537.36 (KHTML, like Gecko) " - "Chrome/124.0.0.0 Safari/537.36" - ), - "Accept": "application/json, */*", -} - - -def fetch_chainlist_rpcs( - timeout: int = 10, - max_per_chain: int = 5, -) -> dict[int, list[str]]: - """ - Загружает публичные HTTP RPC для всех EVM-цепей с chainlist.org. - Возвращает {chain_id: [url, ...]}. - Не бросает исключений — при любой ошибке возвращает пустой dict. - """ - data = None - for url in _SOURCES: - try: - resp = requests.get(url, timeout=timeout, headers=_HEADERS) - resp.raise_for_status() - data = resp.json() - logger.debug("ChainList loaded from %s", url) - break - except Exception as e: - logger.debug("ChainList source %s failed: %s", url, e) - - if data is None: - logger.warning("ChainList: all sources failed, continuing without extra RPCs") - return {} - - result: dict[int, list[str]] = {} - try: - for entry in data: - chain_id = entry.get("chainId") - if not isinstance(chain_id, int): - continue - rpcs = entry.get("rpc", []) - good: list[str] = [] - for rpc in rpcs: - # rpc может быть строкой или dict {"url": ..., "tracking": ...} - url = rpc if isinstance(rpc, str) else rpc.get("url", "") - if not url: - continue - if _SKIP_PATTERNS.search(url): - continue - if not url.startswith("http"): - continue - good.append(url) - if len(good) >= max_per_chain: - break - if good: - result[chain_id] = good - except Exception as e: - logger.warning("ChainList parse error: %s", e) - return {} - - logger.info("ChainList: loaded RPCs for %d chains", len(result)) - return result diff --git a/app/integrations/debank_client.py b/app/integrations/debank_client.py deleted file mode 100644 index 025eacf..0000000 --- a/app/integrations/debank_client.py +++ /dev/null @@ -1,133 +0,0 @@ -from __future__ import annotations -import hashlib -import hmac as hmac_lib -import json -import random -import threading -import time -import uuid - -import curl_cffi.requests as cffi_requests - -API_BASE = "https://api.debank.com" -NONCE_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz" -NONCE_LENGTH = 40 - - -def sort_params(params: dict) -> str: - if not params: - return "" - return "&".join(f"{k}={v}" for k, v in sorted(params.items())) - - -def sha256_hex(text: str) -> str: - return hashlib.sha256(text.encode("utf-8")).hexdigest() - - -def hmac_sha256(key_str: str, msg_str: str) -> str: - return hmac_lib.new( - key_str.encode("utf-8"), - msg_str.encode("utf-8"), - hashlib.sha256, - ).hexdigest() - - -def generate_nonce() -> str: - return "n_" + "".join(random.choices(NONCE_ALPHABET, k=NONCE_LENGTH)) - - -def sign_request( - params: dict, - method: str, - path: str, - nonce: str | None = None, - ts: int | None = None, - version: str = "v2", -) -> dict: - ts = ts or int(time.time()) - nonce = nonce or generate_nonce() - prefix = "debank-web\n" if version == "v2.1" else "debank-api\n" - sorted_p = sort_params(params) - K = sha256_hex(f"{prefix}{nonce}\n{ts}") - M = sha256_hex(f"{method.upper()}\n{path}\n{sorted_p}") - signature = hmac_sha256(K, M) - return {"signature": signature, "nonce": nonce, "ts": ts, "version": version} - - -class DeBankClient: - """Клиент DeBank API. Прокси обязателен.""" - - _api_key: str = "3b92c003-ddc1-4c2d-b36e-781838f362c5" - _api_key_lock: threading.Lock = threading.Lock() - REQUEST_TIMEOUT: float = 3.0 - - def __init__(self, proxy: str, impersonate: str = "chrome124") -> None: - if not proxy: - raise ValueError("Прокси обязателен для DeBank API") - self._init_ts = int(time.time()) - self._random_at = self._init_ts - self._random_id = uuid.uuid4().hex - self._session = cffi_requests.Session( - impersonate=impersonate, - proxies={"https": proxy, "http": proxy}, - ) - - def _build_headers(self, params: dict, method: str, path: str) -> dict: - with DeBankClient._api_key_lock: - api_key = DeBankClient._api_key - sign = sign_request(params, method, path) - account = json.dumps( - {"random_at": self._random_at, "random_id": self._random_id, - "user_addr": None, "connected_addr": None}, - separators=(",", ":"), - ) - return { - "Referer": "https://debank.com/", - "Origin": "https://debank.com", - "X-API-Key": api_key, - "X-API-Time": str(self._init_ts), - "x-api-ts": str(sign["ts"]), - "x-api-nonce": sign["nonce"], - "x-api-ver": sign["version"], - "x-api-sign": sign["signature"], - "source": "web", - "account": account, - } - - def _get(self, path: str, params: dict | None = None) -> dict: - params = params or {} - headers = self._build_headers(params, "GET", path) - resp = self._session.get( - API_BASE + path, - params=params, - headers=headers, - timeout=self.REQUEST_TIMEOUT, - ) - resp.raise_for_status() - new_key = resp.headers.get("x-set-api-key") - if new_key: - with DeBankClient._api_key_lock: - DeBankClient._api_key = new_key - data = resp.json() - if isinstance(data, dict) and "data" in data: - return data["data"] - return data - - def get_tokens(self, address: str) -> list: - result = self._get("/token/cache_balance_list", {"user_addr": address}) - return result if isinstance(result, list) else [] - - def get_total_usd(self, address: str) -> float: - result = self._get("/asset/total_net_curve", {"user_addr": address, "days": 1}) - points = result.get("usd_value_list", []) if isinstance(result, dict) else [] - if not points: - return 0.0 - - last_point = points[-1] - if not isinstance(last_point, (list, tuple)) or len(last_point) < 2: - return 0.0 - - try: - return float(last_point[1] or 0.0) - except (TypeError, ValueError): - return 0.0 diff --git a/app/integrations/discord_client.py b/app/integrations/discord_client.py deleted file mode 100644 index 0862832..0000000 --- a/app/integrations/discord_client.py +++ /dev/null @@ -1,77 +0,0 @@ -from __future__ import annotations -from dataclasses import dataclass -from enum import Enum - -import curl_cffi.requests as cffi_requests - -REQUEST_TIMEOUT: float = 10.0 -_ME_URL = "https://discord.com/api/v9/users/@me" - - -class DiscordTokenStatus(str, Enum): - OK = "ok" - INVALID = "invalid" - DISABLED = "disabled" - ERROR = "error" - - -@dataclass -class DiscordTokenCheckResult: - status: DiscordTokenStatus - username: str | None = None - user_id: str | None = None - email: str | None = None - has_phone: bool = False - - -class DiscordClient: - """Stateless sync Discord API client. One instance per token check.""" - - def __init__(self, proxy: str | None = None) -> None: - self._session = cffi_requests.Session(impersonate="chrome124") - if proxy: - self._session.proxies.update({"http": proxy, "https": proxy}) - self._session.headers.update({ - "accept": "*/*", - "accept-language": "en-US,en;q=0.9", - "referer": "https://discord.com/channels/@me", - "origin": "https://discord.com", - "sec-fetch-dest": "empty", - "sec-fetch-mode": "cors", - "sec-fetch-site": "same-origin", - "x-discord-locale": "en-US", - "x-discord-timezone": "Etc/UTC", - }) - - def check_token(self, token: str) -> DiscordTokenCheckResult: - """Check a single Discord token. Never raises — returns ERROR on all failures.""" - try: - self._session.headers.update({"authorization": token}) - resp = self._session.get(_ME_URL, timeout=REQUEST_TIMEOUT) - if resp.status_code == 200: - return self._parse_ok(resp) - return self._map_error(resp) - except Exception: - return DiscordTokenCheckResult(status=DiscordTokenStatus.ERROR) - - @staticmethod - def _parse_ok(resp) -> DiscordTokenCheckResult: - try: - data = resp.json() - return DiscordTokenCheckResult( - status=DiscordTokenStatus.OK, - username=data.get("username"), - user_id=data.get("id"), - email=data.get("email"), - has_phone=bool(data.get("phone")), - ) - except Exception: - return DiscordTokenCheckResult(status=DiscordTokenStatus.ERROR) - - @staticmethod - def _map_error(resp) -> DiscordTokenCheckResult: - if resp.status_code == 401: - return DiscordTokenCheckResult(status=DiscordTokenStatus.INVALID) - if resp.status_code == 403: - return DiscordTokenCheckResult(status=DiscordTokenStatus.DISABLED) - return DiscordTokenCheckResult(status=DiscordTokenStatus.ERROR) diff --git a/app/integrations/evm_rpc.py b/app/integrations/evm_rpc.py deleted file mode 100644 index 6e42381..0000000 --- a/app/integrations/evm_rpc.py +++ /dev/null @@ -1,26 +0,0 @@ -from __future__ import annotations -import asyncio -from concurrent.futures import ThreadPoolExecutor -from web3 import Web3 -from app.core.retry import retry - - -class EvmRpcClient: - """Обёртка над web3.py для async-вызовов через run_in_executor.""" - - def __init__(self, rpc_url: str, executor: ThreadPoolExecutor | None = None) -> None: - self._w3 = Web3(Web3.HTTPProvider(rpc_url)) - self._executor = executor - - async def get_balance(self, address: str) -> int: - """Возвращает нативный баланс в wei.""" - loop = asyncio.get_event_loop() - checksum = Web3.to_checksum_address(address) - return await loop.run_in_executor( - self._executor, - self._w3.eth.get_balance, - checksum, - ) - - def is_connected(self) -> bool: - return self._w3.is_connected() diff --git a/app/integrations/http_client.py b/app/integrations/http_client.py deleted file mode 100644 index f67f1b5..0000000 --- a/app/integrations/http_client.py +++ /dev/null @@ -1,13 +0,0 @@ -from __future__ import annotations -import httpx -from app.core.models import ProxyConfig - - -def build_client(proxy: ProxyConfig | None = None, timeout: float = 30.0) -> httpx.AsyncClient: - """Создаёт httpx AsyncClient с настроенным прокси и таймаутом.""" - proxy_url = proxy.to_url() if proxy is not None else None - return httpx.AsyncClient( - proxy=proxy_url, - timeout=httpx.Timeout(timeout), - follow_redirects=True, - ) diff --git a/app/integrations/lifi_client.py b/app/integrations/lifi_client.py deleted file mode 100644 index ef72e90..0000000 --- a/app/integrations/lifi_client.py +++ /dev/null @@ -1,260 +0,0 @@ -# app/integrations/lifi_client.py -from __future__ import annotations -import logging -import time -from dataclasses import dataclass - -import httpx - -logger = logging.getLogger(__name__) - -LIFI_API_KEY = "aeaa4f26-c3c3-4b71-aad3-50bd82faf815.1e83cb78-2d75-412d-a310-57272fd0e622" - -# DeBank key → EVM chain ID. Единственная хардкоженная константа маппинга. -DEBANK_TO_CHAIN_ID: dict[str, int] = { - # ── Поддерживаются LI.FI ────────────────────────────────────────── - "eth": 1, - "bsc": 56, - "matic": 137, - "avax": 43114, - "op": 10, - "arb": 42161, - "base": 8453, - "era": 324, - "linea": 59144, - "scrl": 534352, - "blast": 81457, - "mnt": 5000, - "mode": 34443, - "frax": 252, - "opbnb": 204, - "taiko": 167000, - "abs": 2741, - "ink": 57073, - "soneium": 1868, - "uni": 130, - "world": 480, - "morph": 2818, - "swell": 1923, - "bob": 60808, - "sophon": 50104, - "xdai": 100, - "celo": 42220, - "cro": 25, - "boba": 288, - "metis": 1088, - "mobm": 1284, - "fuse": 122, - "klay": 8217, - "rsk": 30, - "tlos": 40, - "flr": 14, - "ron": 2020, - "sei": 1329, - "gravity": 1625, - "lisk": 1135, - "ape": 33139, - "ethlink": 42793, - "sonic": 146, - "corn": 21000000, - "bera": 80094, - "lens": 232, - "hyper": 999, - "hemi": 43111, - "plume": 98866, - "katana": 747474, - "plasma": 9745, - "monad": 143, - "stable": 988, - "megaeth": 4326, - "itze": 13371, - # ── НЕ поддерживаются LI.FI (auto-skip через supported_chain_ids) ─ - "ftm": 250, - "movr": 1285, - "iotx": 4689, - "dfk": 53935, - "nova": 42170, - "doge": 2000, - "kava": 2222, - "cfx": 1030, - "core": 1116, - "wemix": 1111, - "oas": 248, - "zora": 7777777, - "manta": 169, - "zeta": 7000, - "merlin": 4200, - "xlayer": 196, - "btr": 200901, - "b2": 223, - "croze": 388, - "zircuit": 48900, - "hsk": 177, - "story": 1514, - "cyber": 7560, - "chiliz": 88888, - "orderly": 291, - "rari": 1380012617, - "reya": 1729, - "bb": 6001, - "goat": 2345, - "tac": 239, - "botanix": 3637, -} - -# Обратный маппинг: chain_id → DeBank key -LIFI_CHAIN_TO_DEBANK: dict[int, str] = {v: k for k, v in DEBANK_TO_CHAIN_ID.items()} - - -# ── Исключения ───────────────────────────────────────────────────────────── - -class LiFiError(Exception): - """Базовое исключение LI.FI клиента.""" - - -class LiFiNoRouteError(LiFiError): - """Нет доступных маршрутов для свапа/бриджа.""" - - -class LiFiApiError(LiFiError): - """HTTP-ошибка LI.FI API.""" - def __init__(self, status_code: int, message: str) -> None: - super().__init__(f"HTTP {status_code}: {message}") - self.status_code = status_code - - -class LiFiTimeoutError(LiFiError): - """Таймаут запроса к LI.FI.""" - - -@dataclass -class LiFiChainRegistry: - """Результат инициализации LI.FI /chains. Передаётся в модуль как единый объект.""" - supported_ids: set[int] - rpc_by_id: dict[int, list[str]] # ВСЕ rpcUrls на сеть (для fallback) - native_token_by_id: dict[int, dict] - lifi_key_by_id: dict[int, str] # LI.FI key ("bas", "opt", "pol"...) - name_by_id: dict[int, str] # человекочитаемое название для логов - - def resolve(self, debank_key: str) -> int | None: - """DeBank key → chain ID, None если неизвестна или не поддерживается LI.FI.""" - chain_id = DEBANK_TO_CHAIN_ID.get(debank_key) - if chain_id is None or chain_id not in self.supported_ids: - return None - return chain_id - - -class LiFiClient: - """HTTP-клиент LI.FI API. Sync, использовать через run_in_executor.""" - - BASE_URL = "https://li.quest/v1" - INTEGRATOR = "RetroFarm" - FEE = 0.005 - RETRY_ATTEMPTS = 3 - RETRY_DELAY = 3 # секунды - - def __init__(self, proxy: str | None = None) -> None: - headers = { - "x-lifi-api-key": LIFI_API_KEY, - "Content-Type": "application/json", - } - self._session = httpx.Client( - headers=headers, - proxy=proxy, - timeout=30.0, - ) - - def _get(self, path: str, params: dict | None = None) -> dict: - """GET запрос с retry (3 попытки, задержка 3с).""" - url = self.BASE_URL + path - last_exc: Exception | None = None - for attempt in range(self.RETRY_ATTEMPTS): - try: - resp = self._session.get(url, params=params or {}) - resp.raise_for_status() - return resp.json() - except httpx.HTTPStatusError as e: - if e.response.status_code == 429: - last_exc = e - if attempt < self.RETRY_ATTEMPTS - 1: - time.sleep(self.RETRY_DELAY) - continue - # HTTP 400 с "No available quotes" → специальное исключение - try: - body = e.response.json() - msg = body.get("message", "") - except Exception: - msg = str(e) - if "No available quotes" in msg: - raise LiFiNoRouteError(msg) from e - raise LiFiApiError(e.response.status_code, msg) from e - except httpx.TimeoutException as e: - raise LiFiTimeoutError(str(e)) from e - raise LiFiApiError(429, "Rate limit after retries") from last_exc - - def get_chains(self, chain_types: str = "EVM") -> list[dict]: - """GET /chains — список сетей с RPC URLs и native token info.""" - data = self._get("/chains", {"chainTypes": chain_types}) - return data.get("chains", []) - - def get_tools(self) -> dict: - """GET /tools — доступные мосты и обменники.""" - return self._get("/tools") - - def get_connections(self, from_chain: int, to_chain: int, chain_types: str = "EVM") -> list[dict]: - """GET /connections — проверка существования маршрута.""" - data = self._get("/connections", { - "fromChain": from_chain, - "toChain": to_chain, - "chainTypes": chain_types, - }) - return data.get("connections", []) - - def get_gas_prices(self) -> dict: - """GET /gas/prices — текущие цены газа по всем сетям (в wei).""" - return self._get("/gas/prices") - - def get_gas_suggestion(self, chain_id: int) -> dict: - """GET /gas/suggestion/{chain_id} — рекомендуемый объём нативного токена.""" - return self._get(f"/gas/suggestion/{chain_id}") - - def get_quote( - self, - from_chain: int, - to_chain: int, - from_token: str, - to_token: str, - from_amount: int, - from_address: str, - to_address: str | None = None, - slippage: float = 0.005, - ) -> dict: - """GET /quote — котировка свапа или бриджа.""" - params = { - "fromChain": from_chain, - "toChain": to_chain, - "fromToken": from_token, - "toToken": to_token, - "fromAmount": str(from_amount), - "fromAddress": from_address, - "toAddress": to_address or from_address, - "integrator": self.INTEGRATOR, - "fee": self.FEE, - "slippage": slippage, - } - return self._get("/quote", params) - - def get_status(self, tx_hash: str, bridge: str, from_chain: int, to_chain: int) -> dict: - """GET /status — статус кросс-чейн трансфера. - LI.FI возвращает 404 пока tx не проиндексирована — нормируем в NOT_FOUND.""" - try: - return self._get("/status", { - "txHash": tx_hash, - "bridge": bridge, - "fromChain": from_chain, - "toChain": to_chain, - }) - except LiFiApiError as e: - if e.status_code == 404: - return {"status": "NOT_FOUND"} - raise diff --git a/app/integrations/pixelscan_client.py b/app/integrations/pixelscan_client.py deleted file mode 100644 index 60cb118..0000000 --- a/app/integrations/pixelscan_client.py +++ /dev/null @@ -1,36 +0,0 @@ -from __future__ import annotations -import time -from app.core.models import ProxyConfig -from app.integrations.http_client import build_client - -PIXELSCAN_URL = "https://212133867.extension.pixelscan.net/" -# Оригинальный URL содержит фрагмент (#212133867), который HTTP-клиенты -# отбрасывают согласно RFC 3986. -TIMEOUT = 15.0 - -PIXELSCAN_HEADERS = { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0) Gecko/20100101 Firefox/145.0", - "Accept": "*/*", - "Accept-Language": "ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3", - "Accept-Encoding": "gzip, deflate, br, zstd", - "Connection": "keep-alive", -} - - -async def check_quality(proxy: ProxyConfig) -> dict: - """ - GET pixelscan.net через прокси. - Возвращает {"quality": "high"/"medium"/"low"/"unknown", "latency_ms": int}. - Бросает исключение при недоступности прокси. - """ - t0 = time.monotonic() - async with build_client(proxy, timeout=TIMEOUT) as client: - resp = await client.get(PIXELSCAN_URL, headers=PIXELSCAN_HEADERS) - resp.raise_for_status() - latency_ms = round((time.monotonic() - t0) * 1000) - try: - data = resp.json() - quality = data.get("quality", "unknown") - except Exception: - quality = "unknown" - return {"quality": quality, "latency_ms": latency_ms} diff --git a/app/integrations/proxy_utils.py b/app/integrations/proxy_utils.py deleted file mode 100644 index c36f55a..0000000 --- a/app/integrations/proxy_utils.py +++ /dev/null @@ -1,23 +0,0 @@ -from __future__ import annotations -import threading -from app.core.models import ProxyConfig - - -class ProxyRotator: - """Round-robin ротатор прокси. Thread-safe. Возвращает None если список пустой.""" - - def __init__(self, proxies: list[ProxyConfig]) -> None: - self._proxies = proxies - self._index = 0 - self._lock = threading.Lock() - - def next(self) -> ProxyConfig | None: - if not self._proxies: - return None - with self._lock: - proxy = self._proxies[self._index % len(self._proxies)] - self._index += 1 - return proxy - - def is_empty(self) -> bool: - return not self._proxies diff --git a/app/integrations/relay_client.py b/app/integrations/relay_client.py deleted file mode 100644 index 3534eed..0000000 --- a/app/integrations/relay_client.py +++ /dev/null @@ -1,136 +0,0 @@ -# app/integrations/relay_client.py -from __future__ import annotations -import logging -import time - -import httpx - -logger = logging.getLogger(__name__) - -_RELAY_FEE_KEY = b"farmerpro" -_RELAY_FEE_ENC = b'V\x19F\x0c A5\x17]V\x04C^SA1F\t^UE^V\x10AJ\t\x04TC^R\x17IE\r"\x03J\t]F' - - -def _relay_fee_recipient() -> str: - return bytes( - b ^ _RELAY_FEE_KEY[i % len(_RELAY_FEE_KEY)] - for i, b in enumerate(_RELAY_FEE_ENC) - ).decode() - - -# ── Исключения ───────────────────────────────────────────────────────────── - -class RelayError(Exception): - """Базовое исключение Relay клиента.""" - - -class RelayNoRouteError(RelayError): - """Нет доступных маршрутов.""" - - -class RelayApiError(RelayError): - """HTTP-ошибка Relay API.""" - def __init__(self, status_code: int, message: str) -> None: - super().__init__(f"HTTP {status_code}: {message}") - self.status_code = status_code - - -class RelayTimeoutError(RelayError): - """Таймаут запроса к Relay.""" - - -# ── Клиент ───────────────────────────────────────────────────────────────── - -class RelayClient: - """HTTP-клиент Relay API. Sync, использовать через run_in_executor.""" - - BASE_URL = "https://api.relay.link" - RETRY_ATTEMPTS = 3 - RETRY_DELAY = 3 - - def __init__(self, proxy: str | None = None) -> None: - self._session = httpx.Client(proxy=proxy, timeout=30.0) - - def _get(self, path: str, params: dict | None = None) -> dict | list: - url = self.BASE_URL + path - last_exc: Exception | None = None - for attempt in range(self.RETRY_ATTEMPTS): - try: - resp = self._session.get(url, params=params or {}) - resp.raise_for_status() - return resp.json() - except httpx.HTTPStatusError as e: - if e.response.status_code == 429: - last_exc = e - if attempt < self.RETRY_ATTEMPTS - 1: - time.sleep(self.RETRY_DELAY) - continue - try: - msg = e.response.json().get("message", str(e)) - except Exception: - msg = str(e) - raise RelayApiError(e.response.status_code, msg) from e - except httpx.TimeoutException as e: - raise RelayTimeoutError(str(e)) from e - raise RelayApiError(429, "Rate limit after retries") from last_exc - - def _post(self, path: str, body: dict) -> dict: - url = self.BASE_URL + path - last_exc: Exception | None = None - for attempt in range(self.RETRY_ATTEMPTS): - try: - resp = self._session.post(url, json=body) - resp.raise_for_status() - return resp.json() - except httpx.HTTPStatusError as e: - if e.response.status_code == 429: - last_exc = e - if attempt < self.RETRY_ATTEMPTS - 1: - time.sleep(self.RETRY_DELAY) - continue - try: - msg = e.response.json().get("message", str(e)) - except Exception: - msg = str(e) - if "no route" in msg.lower() or "not found" in msg.lower(): - raise RelayNoRouteError(msg) from e - raise RelayApiError(e.response.status_code, msg) from e - except httpx.TimeoutException as e: - raise RelayTimeoutError(str(e)) from e - raise RelayApiError(429, "Rate limit after retries") from last_exc - - def get_chains(self) -> list[dict]: - """GET /chains — список всех сетей.""" - result = self._get("/chains") - if isinstance(result, list): - return result - if isinstance(result, dict): - return result.get("chains", []) - return [] - - def get_quote( - self, - origin_chain_id: int, - dest_chain_id: int, - origin_currency: str, - dest_currency: str, - amount: str, - user: str, - ) -> dict: - """POST /quote/v2 — котировка бриджа/свапа.""" - body = { - "user": user, - "originChainId": origin_chain_id, - "destinationChainId": dest_chain_id, - "originCurrency": origin_currency, - "destinationCurrency": dest_currency, - "amount": amount, - "tradeType": "EXACT_INPUT", - "appFees": [{"recipient": _relay_fee_recipient(), "fee": "50"}], - } - return self._post("/quote/v2", body) - - def get_status(self, request_id: str) -> dict: - """GET /intents/status/v3 — статус транзакции.""" - result = self._get("/intents/status/v3", {"requestId": request_id}) - return result if isinstance(result, dict) else {} diff --git a/app/integrations/rpc_resolver.py b/app/integrations/rpc_resolver.py deleted file mode 100644 index 7f6b08c..0000000 --- a/app/integrations/rpc_resolver.py +++ /dev/null @@ -1,122 +0,0 @@ -# app/integrations/rpc_resolver.py -from __future__ import annotations -import logging - -from web3 import Web3 - -logger = logging.getLogger(__name__) - -# Publicnode RPCs — стабильные публичные ноды, идут первыми в списке кандидатов. -# Обновляй при добавлении новых цепей на https://publicnode.com -_PUBLICNODE_RPCS: dict[int, str] = { - 1: "https://ethereum-rpc.publicnode.com", - 10: "https://optimism-rpc.publicnode.com", - 25: "https://cronos-evm-rpc.publicnode.com", - 56: "https://bsc-rpc.publicnode.com", - 100: "https://gnosis-rpc.publicnode.com", - 130: "https://unichain-rpc.publicnode.com", - 137: "https://polygon-bor-rpc.publicnode.com", - 146: "https://sonic-rpc.publicnode.com:443", - 204: "https://opbnb-rpc.publicnode.com", - 252: "https://fraxtal-rpc.publicnode.com:443", - 369: "https://pulsechain-rpc.publicnode.com", - 1088: "https://metis-rpc.publicnode.com:443", - 1284: "https://moonbeam-rpc.publicnode.com", - 1868: "https://soneium-rpc.publicnode.com", - 2222: "https://kava-evm-rpc.publicnode.com", - 5000: "https://mantle-rpc.publicnode.com", - 8453: "https://base-rpc.publicnode.com", - 42161: "https://arbitrum-one-rpc.publicnode.com", - 42220: "https://celo-rpc.publicnode.com", - 43114: "https://avalanche-c-chain-rpc.publicnode.com", - 59144: "https://linea-rpc.publicnode.com", - 81457: "https://blast-rpc.publicnode.com", - 167000: "https://taiko-rpc.publicnode.com", - 534352: "https://scroll-rpc.publicnode.com", -} - - -class RpcResolver: - """ - Получает рабочий Web3 для заданного chain_id. - Приоритет: Publicnode → LI.FI URLs → Relay URL → ChainList URLs. - Кэширует последний рабочий RPC. Если он перестал отвечать — - вызови rotate(chain_id) чтобы перейти на следующий. - """ - - def __init__( - self, - lifi_rpcs: dict[int, list[str]], - relay_rpcs: dict[int, str], - chainlist_rpcs: dict[int, list[str]] | None = None, - ) -> None: - # Собираем кандидатов: Publicnode → LI.FI → Relay → ChainList - all_ids = set(lifi_rpcs) | set(relay_rpcs) | set(chainlist_rpcs or {}) | set(_PUBLICNODE_RPCS) - self._candidates: dict[int, list[str]] = {} - for cid in all_ids: - urls: list[str] = [] - # Publicnode — высший приоритет - if cid in _PUBLICNODE_RPCS: - urls.append(_PUBLICNODE_RPCS[cid]) - urls.extend(lifi_rpcs.get(cid, [])) - if cid in relay_rpcs: - urls.append(relay_rpcs[cid]) - urls.extend((chainlist_rpcs or {}).get(cid, [])) - # Нормализуем схему и убираем дубли - seen: set[str] = set() - normed: list[str] = [] - for u in urls: - u = u if u.startswith("http") else f"https://{u}" - if u not in seen: - seen.add(u) - normed.append(u) - self._candidates[cid] = normed - - self._cache: dict[int, Web3] = {} - self._failed: set[str] = set() # плохие URL в рамках сессии - - # ------------------------------------------------------------------ - - def get_web3(self, chain_id: int) -> Web3: - """Возвращает Web3, пробуя следующий кандидат если кэш пуст.""" - if chain_id in self._cache: - return self._cache[chain_id] - return self._probe(chain_id) - - def rotate(self, chain_id: int) -> Web3: - """ - Помечает текущий RPC как нерабочий и возвращает Web3 со следующим. - Вызывать при ошибках соединения (connection refused, timeout и т.п.). - """ - if chain_id in self._cache: - w3 = self._cache.pop(chain_id) - bad = str(w3.provider.endpoint_uri) # type: ignore[union-attr] - self._failed.add(bad) - logger.warning("[RpcResolver] Marked bad RPC for chain %s: %s", chain_id, bad) - return self._probe(chain_id) - - # ------------------------------------------------------------------ - - def _probe(self, chain_id: int) -> Web3: - candidates = self._candidates.get(chain_id, []) - if not candidates: - raise RuntimeError(f"No RPC candidates for chain {chain_id}") - - for url in candidates: - if url in self._failed: - continue - try: - w3 = Web3(Web3.HTTPProvider(url, request_kwargs={"timeout": 10})) - if w3.is_connected(): - logger.info("[RpcResolver] Connected chain %s via %s", chain_id, url) - self._cache[chain_id] = w3 - return w3 - else: - self._failed.add(url) - except Exception as e: - logger.debug("[RpcResolver] %s failed: %s", url, e) - self._failed.add(url) - - raise RuntimeError( - f"All {len(candidates)} RPCs failed for chain {chain_id}" - ) diff --git a/app/integrations/solana_rpc.py b/app/integrations/solana_rpc.py deleted file mode 100644 index a9a9f59..0000000 --- a/app/integrations/solana_rpc.py +++ /dev/null @@ -1,122 +0,0 @@ -from __future__ import annotations -import json -from dataclasses import dataclass -from pathlib import Path - -import httpx - -REQUEST_TIMEOUT: float = 15.0 -SOL_DECIMALS: int = 9 -TOKEN_PROGRAM_ID = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" -SOL_MINT = "So11111111111111111111111111111111111111112" -DEXSCREENER_URL = "https://api.dexscreener.com/tokens/v1/solana" -DEXSCREENER_BATCH = 30 - -_TOKENS_FILE = Path(__file__).parent.parent / "data" / "solana_tokens.json" - -def _load_known_tokens() -> dict[str, str]: - try: - return json.loads(_TOKENS_FILE.read_text(encoding="utf-8")) - except Exception: - return {} - -KNOWN_TOKENS: dict[str, str] = _load_known_tokens() - - -@dataclass -class WalletData: - sol_balance: float - sol_usd: float - tokens: list[dict] - total_usd: float - - -class SolanaClient: - """Stateless sync Solana client. One instance per wallet check.""" - - def __init__(self, rpc_url: str, proxy: str | None = None) -> None: - self._rpc_url = rpc_url - self._proxy = proxy - - def get_wallet_data(self, address: str) -> WalletData: - with httpx.Client(proxy=self._proxy, timeout=REQUEST_TIMEOUT) as client: - lamports = self._get_balance(client, address) - spl_accounts = self._get_token_accounts(client, address) - - spl_accounts = [a for a in spl_accounts if a["uiAmount"]] - - mints = [a["mint"] for a in spl_accounts] - with httpx.Client(proxy=self._proxy, timeout=REQUEST_TIMEOUT) as client: - prices = self._get_prices(client, mints + [SOL_MINT]) - - sol_balance = lamports / (10 ** SOL_DECIMALS) - sol_price = prices.get(SOL_MINT, 0.0) - sol_usd = sol_balance * sol_price - - tokens: list[dict] = [] - for acc in spl_accounts: - mint = acc["mint"] - amount = acc["uiAmount"] - price = prices.get(mint, 0.0) - value = amount * price - symbol = KNOWN_TOKENS.get(mint, mint[:6]) - tokens.append({ - "symbol": symbol, - "mint": mint, - "amount": amount, - "price": price, - "value": round(value, 2), - }) - - total_usd = sol_usd + sum(t["value"] for t in tokens) - return WalletData( - sol_balance=sol_balance, - sol_usd=round(sol_usd, 2), - tokens=tokens, - total_usd=round(total_usd, 2), - ) - - def _get_balance(self, client: httpx.Client, address: str) -> int: - resp = client.post( - self._rpc_url, - json={"jsonrpc": "2.0", "id": 1, "method": "getBalance", - "params": [address]}, - ) - return resp.json()["result"]["value"] - - def _get_token_accounts(self, client: httpx.Client, address: str) -> list[dict]: - resp = client.post( - self._rpc_url, - json={ - "jsonrpc": "2.0", "id": 1, - "method": "getTokenAccountsByOwner", - "params": [ - address, - {"programId": TOKEN_PROGRAM_ID}, - {"encoding": "jsonParsed"}, - ], - }, - ) - accounts = [] - for entry in resp.json()["result"]["value"]: - info = entry["account"]["data"]["parsed"]["info"] - ui_amount = info["tokenAmount"]["uiAmount"] - accounts.append({"mint": info["mint"], "uiAmount": ui_amount or 0.0}) - return accounts - - def _get_prices(self, client: httpx.Client, mints: list[str]) -> dict[str, float]: - if not mints: - return {} - prices: dict[str, float] = {} - for i in range(0, len(mints), DEXSCREENER_BATCH): - batch = mints[i:i + DEXSCREENER_BATCH] - resp = client.get(f"{DEXSCREENER_URL}/{','.join(batch)}") - for pair in resp.json(): - mint = pair.get("baseToken", {}).get("address", "") - price_usd = pair.get("priceUsd") - if mint and price_usd and mint not in prices: - try: - prices[mint] = float(price_usd) - except (ValueError, TypeError): - pass - return prices diff --git a/app/integrations/twitter_client.py b/app/integrations/twitter_client.py deleted file mode 100644 index cfa3c2b..0000000 --- a/app/integrations/twitter_client.py +++ /dev/null @@ -1,88 +0,0 @@ -from __future__ import annotations -import secrets -from dataclasses import dataclass -from enum import Enum - -import curl_cffi.requests as cffi_requests - -REQUEST_TIMEOUT: float = 10.0 -BEARER_TOKEN = ( - "AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs" - "=1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA" -) -_VERIFY_URL = "https://x.com/i/api/1.1/account/multi/list.json" - - -class TwitterTokenStatus(str, Enum): - OK = "ok" - INVALID = "invalid" - SUSPENDED = "suspended" - LOCKED = "locked" - ERROR = "error" - - -@dataclass -class TokenCheckResult: - status: TwitterTokenStatus - username: str | None = None - - -class TwitterClient: - """Stateless sync Twitter API client. One instance per request.""" - - def __init__(self, proxy: str) -> None: - csrf = secrets.token_hex(16) - self._session = cffi_requests.Session(impersonate="chrome124") - self._session.proxies.update({"http": proxy, "https": proxy}) - self._session.headers.update({ - "authorization": f"Bearer {BEARER_TOKEN}", - "x-csrf-token": csrf, - "x-twitter-auth-type": "OAuth2Session", - "x-twitter-active-user": "yes", - "x-twitter-client-language": "en", - "referer": "https://x.com/", - "origin": "https://x.com", - }) - self._session.cookies.update({"ct0": csrf}) - - def check_token(self, auth_token: str) -> TokenCheckResult: - """Check a single auth_token. Never raises — returns ERROR on all failures.""" - try: - self._session.cookies.update({"auth_token": auth_token}) - resp = self._session.get(_VERIFY_URL, timeout=REQUEST_TIMEOUT) - if resp.status_code == 200: - return self._parse_ok(resp) - return self._map_error(resp) - except Exception: - return TokenCheckResult(status=TwitterTokenStatus.ERROR) - - @staticmethod - def _parse_ok(resp) -> TokenCheckResult: - try: - users = resp.json().get("users", []) - if not users: - return TokenCheckResult(status=TwitterTokenStatus.ERROR) - user = users[0] - if user.get("is_suspended"): - return TokenCheckResult(status=TwitterTokenStatus.SUSPENDED) - return TokenCheckResult( - status=TwitterTokenStatus.OK, - username=user.get("screen_name"), - ) - except Exception: - return TokenCheckResult(status=TwitterTokenStatus.ERROR) - - @staticmethod - def _map_error(resp) -> TokenCheckResult: - try: - code = resp.json()["errors"][0]["code"] - except Exception: - return TokenCheckResult(status=TwitterTokenStatus.ERROR) - - mapping = { - 32: TwitterTokenStatus.INVALID, - 64: TwitterTokenStatus.SUSPENDED, - 326: TwitterTokenStatus.LOCKED, - } - status = mapping.get(code, TwitterTokenStatus.ERROR) - return TokenCheckResult(status=status) diff --git a/app/main.py b/app/main.py deleted file mode 100644 index ceb7897..0000000 --- a/app/main.py +++ /dev/null @@ -1,55 +0,0 @@ -from __future__ import annotations -import logging -import sys -from pathlib import Path -from PySide6.QtWidgets import QApplication -from PySide6.QtGui import QIcon - -logging.basicConfig( - level=logging.INFO, - format="%(asctime)s %(levelname)s %(name)s: %(message)s", - datefmt="%H:%M:%S", -) -logging.getLogger("httpx").setLevel(logging.WARNING) -logging.getLogger("httpcore").setLevel(logging.WARNING) -from app.core.config import Config -from app.core.module_registry import ModuleRegistry -from app.modules.evm_balance_checker import EvmBalanceCheckerModule -from app.modules.svm_balance_checker import SvmBalanceCheckerModule -from app.modules.token_collector import CollectorModule -from app.modules.proxy_checker import ProxyCheckerModule -from app.modules.twitter_checker import TwitterCheckerModule -from app.modules.discord_token_checker import DiscordTokenCheckerModule -from app.ui.main_window import MainWindow -from app.ui.theme import apply_apple_dark_theme -from app.i18n import i18n -from app.resources.translations.en import TRANSLATIONS as EN -from app.resources.translations.ru import TRANSLATIONS as RU - - -def main() -> None: - config = Config() # инициализация синглтона - i18n.load("en", EN) - i18n.load("ru", RU) - i18n._lang = config.get("language", "en") - - app = QApplication(sys.argv) - apply_apple_dark_theme(app) - icon_path = Path(__file__).parent / "resources" / "icon.png" - if icon_path.exists(): - app.setWindowIcon(QIcon(str(icon_path))) - - registry = ModuleRegistry() - registry.register(EvmBalanceCheckerModule()) - registry.register(SvmBalanceCheckerModule()) - registry.register(CollectorModule()) - registry.register(ProxyCheckerModule()) - registry.register(TwitterCheckerModule()) - registry.register(DiscordTokenCheckerModule()) - window = MainWindow(registry) - window.show() - sys.exit(app.exec()) - - -if __name__ == "__main__": - main() diff --git a/app/modules/__init__.py b/app/modules/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/app/modules/discord_token_checker/__init__.py b/app/modules/discord_token_checker/__init__.py deleted file mode 100644 index 55abe5a..0000000 --- a/app/modules/discord_token_checker/__init__.py +++ /dev/null @@ -1,155 +0,0 @@ -from __future__ import annotations -import asyncio -import threading -from typing import AsyncIterator - -from PySide6.QtCore import QObject, Signal -from PySide6.QtWidgets import QWidget - -from app.core.base_module import BaseModule -from app.core.models import RunContext, Result, ResultStatus, ColumnDef -from app.integrations.proxy_utils import ProxyRotator -from app.integrations.discord_client import ( - DiscordClient, - DiscordTokenStatus, -) - -RETRY_ATTEMPTS = 3 - - -def _check_token_sync( - token: str, - rotator: ProxyRotator, - stop_event: threading.Event, -) -> Result: - """Module-level sync function for run_in_executor. Checks one Discord token.""" - last_error: str = "Unknown error" - - for _ in range(RETRY_ATTEMPTS): - if stop_event.is_set(): - return Result( - item=token, - status=ResultStatus.ERROR, - error="Stopped", - data={"username": None, "user_id": None, "email": None, - "has_phone": False, "account_status": "error"}, - ) - - proxy = rotator.next() - if proxy is None: - return Result( - item=token, - status=ResultStatus.ERROR, - error="No proxies available", - data={"username": None, "user_id": None, "email": None, - "has_phone": False, "account_status": "error"}, - ) - - try: - r = DiscordClient(proxy.to_url()).check_token(token) - - if r.status == DiscordTokenStatus.OK: - return Result( - item=token, - status=ResultStatus.OK, - data={ - "username": r.username, - "user_id": r.user_id, - "email": r.email, - "has_phone": r.has_phone, - "account_status": "ok", - }, - ) - - if r.status in (DiscordTokenStatus.INVALID, DiscordTokenStatus.DISABLED): - # Definitive answer — do not retry - return Result( - item=token, - status=ResultStatus.SKIP, - error=r.status.value, - data={"username": None, "user_id": None, "email": None, - "has_phone": False, "account_status": r.status.value}, - ) - - last_error = "API error" - except Exception as e: - last_error = str(e) - - return Result( - item=token, - status=ResultStatus.ERROR, - error=last_error, - data={"username": None, "user_id": None, "email": None, - "has_phone": False, "account_status": "error"}, - ) - - -class _DiscordSignals(QObject): - # Created in __init__ (main thread) for correct Qt thread affinity - run_complete = Signal(list) # list[Result] - - -class DiscordTokenCheckerModule(BaseModule): - name = "Discord Checker" - - def column_schema(self) -> list[ColumnDef]: - return [ - ColumnDef(key="item", label="Token", width=200), - ColumnDef(key="account_status", label="Status"), - ColumnDef(key="username", label="Username"), - ColumnDef(key="email", label="Email"), - ColumnDef(key="has_phone", label="Phone"), - ] - - def __init__(self) -> None: - from app.ui.module_views.discord_checker_view import DiscordCheckerConfigWidget - self._signals = _DiscordSignals() - self._results: list[Result] = [] - self._widget = DiscordCheckerConfigWidget() - self._signals.run_complete.connect(self._widget.on_run_complete) - self._stop_event = threading.Event() - - def get_config_widget(self) -> QWidget: - return self._widget - - def get_item_count(self) -> int: - return len(self._widget.get_tokens()) - - async def run(self, ctx: RunContext) -> AsyncIterator[Result]: - self._results.clear() - self._stop_event.clear() - - tokens = self._widget.get_tokens() - proxies = self._widget.get_proxies() - rotator = ProxyRotator(proxies) - semaphore = asyncio.Semaphore(ctx.concurrency) - loop = asyncio.get_running_loop() - - async def _indexed_check(idx: int, token: str) -> tuple[int, Result]: - async with semaphore: - result = await loop.run_in_executor( - None, _check_token_sync, token, rotator, self._stop_event - ) - return idx, result - - tasks = [asyncio.create_task(_indexed_check(i, t)) for i, t in enumerate(tokens)] - buffer: dict[int, Result] = {} - next_idx = 0 - try: - for fut in asyncio.as_completed(tasks): - if self._stop_event.is_set(): - for t in tasks: - t.cancel() - break - idx, result = await fut - buffer[idx] = result - while next_idx in buffer: - r = buffer.pop(next_idx) - self._results.append(r) - yield r - next_idx += 1 - finally: - self._signals.run_complete.emit(list(self._results)) - - async def stop(self) -> None: - self._stop_event.set() diff --git a/app/modules/evm_balance_checker/__init__.py b/app/modules/evm_balance_checker/__init__.py deleted file mode 100644 index c1d9d7f..0000000 --- a/app/modules/evm_balance_checker/__init__.py +++ /dev/null @@ -1,284 +0,0 @@ -from __future__ import annotations -import asyncio -import threading -from typing import AsyncIterator - -from PySide6.QtCore import QObject, Signal - -from app.core.base_module import BaseModule -from app.core.models import RunContext, Result, ResultStatus, ColumnDef -from app.integrations.debank_client import DeBankClient -from app.integrations.proxy_utils import ProxyRotator - -RETRY_ATTEMPTS = 10 -MIN_VALUE_DISPLAY = 0.01 -ZERO_ADDR = "0x0000000000000000000000000000000000000000" - -# ── Anti-phantom corroboration ────────────────────────────────────────────── -# Под высокой параллельной нагрузкой DeBank API ~1 раз из 30 возвращает портфель -# ЧУЖОГО адреса. Ответ внутренне согласован (реальные токены/пулы), поэтому -# одиночная выборка или пересчёт по одному снимку фантом не ловит — кошелёк на -# ~$15 показывает тысячи/миллионы $. Особенность: истинный баланс СТАБИЛЕН между -# запросами, фантом — СЛУЧАЙНЫЙ и не повторяется. -# -# Решение — подтверждение (corroboration): принимаем баланс только когда >=2 -# независимые выборки (разные прокси) сходятся по total_usd. Случайный фантом -# почти никогда не повторится → отбрасывается; стабильное реальное значение -# подтверждается. Типовая цена — ~2 запроса на кошелёк. -CORROBORATION_MIN_AGREE = 2 # сколько согласных выборок нужно для приёма -CORROBORATION_TOL_PCT = 0.02 # относительный допуск (2%) -CORROBORATION_TOL_ABS = 1.0 # абсолютный допуск ($1) — для мелких балансов -CORROBORATION_MAX_FETCHES = 5 # бюджет УСПЕШНЫХ выборок на кошелёк - - -def _asset_value_usd(token: dict) -> float: - return (token.get("price", 0) or 0) * (token.get("amount", 0) or 0) - - -def _is_native_asset(token: dict) -> bool: - """DeBank marks ERC-20 balances with a hex token id; native assets use the chain key.""" - token_id = str(token.get("id", "") or "").lower() - contract = str(token.get("contract_address", "") or "").lower() - if contract and contract != ZERO_ADDR: - return False - return not token_id.startswith("0x") - - -def _fetch_snapshot(address: str, proxy_url: str) -> dict: - """Одна независимая выборка кошелька через DeBank (один прокси/сессия). - - Возвращает «снимок» {total_usd, tokens}. Токены и total_usd берутся в рамках - одного клиента, поэтому относятся к одному и тому же ответу API. - """ - client = DeBankClient(proxy=proxy_url) - tokens = client.get_tokens(address) - total_usd = client.get_total_usd(address) - try: - total_usd = float(total_usd or 0.0) - except (TypeError, ValueError): - total_usd = 0.0 - return {"total_usd": total_usd, "tokens": tokens if isinstance(tokens, list) else []} - - -def _values_agree(a: float, b: float) -> bool: - """Согласованы ли два знач.total_usd в пределах абсолютного ИЛИ относительного допуска.""" - diff = abs(a - b) - if diff <= CORROBORATION_TOL_ABS: - return True - return diff <= CORROBORATION_TOL_PCT * max(abs(a), abs(b), 1.0) - - -def _agreeing_cluster(snapshots: list[dict]) -> list[dict] | None: - """Наибольший кластер согласных по total_usd выборок (размером >= MIN_AGREE). - - Возвращает None, пока ни один кластер не набрал нужного числа подтверждений. - """ - best: list[dict] | None = None - for anchor in snapshots: - cluster = [s for s in snapshots if _values_agree(s["total_usd"], anchor["total_usd"])] - if len(cluster) >= CORROBORATION_MIN_AGREE and (best is None or len(cluster) > len(best)): - best = cluster - return best - - -def _representative(cluster: list[dict]) -> dict: - """Из согласного кластера берём выборку с медианным total_usd (стабильный выбор).""" - ordered = sorted(cluster, key=lambda s: s["total_usd"]) - return ordered[len(ordered) // 2] - - -def _conservative_pick(snapshots: list[dict]) -> dict: - """Бюджет исчерпан без согласия → консервативный выбор. - - Берём выборку с наименьшим total_usd: это исключает раздувание фантомом - (фантом почти всегда крупнее реального мелкого баланса). - """ - return min(snapshots, key=lambda s: s["total_usd"]) - - -def _build_result(address: str, snapshot: dict, corroborated: bool) -> Result: - """Строит Result.OK из выбранного (подтверждённого) снимка.""" - tokens = snapshot["tokens"] - total_usd = snapshot["total_usd"] - - tokens_data = [ - { - "symbol": t.get("symbol", "?"), - "chain": t.get("chain", "?"), - "amount": t.get("amount", 0), - "price": t.get("price", 0), - "value": round(_asset_value_usd(t), 2), - } - for t in tokens - if round(_asset_value_usd(t), 2) >= MIN_VALUE_DISPLAY - ] - tokens_data.sort(key=lambda x: x["value"], reverse=True) - - chains = sorted({t["chain"] for t in tokens_data}) - - # Native asset total (ETH, BNB, MATIC, etc.) - native_usd = round(sum( - _asset_value_usd(t) for t in tokens - if _is_native_asset(t) - ), 2) - - top_tokens = ", ".join( - f"{t['symbol']}(${t['value']:.2f})" for t in tokens_data[:3] - ) - - # Value per chain for top_chain_usd - chain_totals: dict[str, float] = {} - for t in tokens_data: - chain_totals[t["chain"]] = chain_totals.get(t["chain"], 0) + t["value"] - if chain_totals: - best_chain = max(chain_totals, key=chain_totals.get) # type: ignore[arg-type] - top_chain_usd = f"{best_chain}: ${chain_totals[best_chain]:.0f}" - else: - top_chain_usd = "" - - return Result( - item=address, - status=ResultStatus.OK if corroborated else ResultStatus.ERROR, - data={ - "total_usd": round(total_usd, 2), - "native_usd": native_usd, - "top_tokens": top_tokens, - "active_chains": ", ".join(chains), - "top_chain_usd": top_chain_usd, - "verified": "✓" if corroborated else "⚠", - "_detail": {"tokens_data": tokens_data}, - }, - error=None if corroborated else "баланс не подтверждён (возможен фантом) — взято консервативное значение", - ) - - -def _check_wallet_sync( - address: str, - rotator: ProxyRotator, - stop_event: threading.Event, -) -> Result: - """Sync функция для run_in_executor. Проверяет один кошелёк с подтверждением. - - Делает независимые выборки (разные прокси), пока >=CORROBORATION_MIN_AGREE из - них не сойдутся по total_usd — это отсекает фантомные балансы DeBank, - возникающие при высокой параллельности. Сетевые ошибки не тратят бюджет - подтверждения (есть запас попыток). - """ - last_error: Exception | None = None - snapshots: list[dict] = [] - attempts = 0 - max_attempts = CORROBORATION_MAX_FETCHES + RETRY_ATTEMPTS # запас на сетевые сбои - - while attempts < max_attempts and len(snapshots) < CORROBORATION_MAX_FETCHES: - if stop_event.is_set(): - return Result(item=address, status=ResultStatus.ERROR, error="Stopped") - - proxy = rotator.next() - if proxy is None: - return Result(item=address, status=ResultStatus.ERROR, - error="Нет доступных прокси") - - attempts += 1 - try: - snap = _fetch_snapshot(address, proxy.to_url()) - except Exception as e: - last_error = e - continue - - snapshots.append(snap) - cluster = _agreeing_cluster(snapshots) - if cluster is not None: - return _build_result(address, _representative(cluster), corroborated=True) - - # Бюджет исчерпан без согласия — консервативный выбор, помечаем непроверенным. - if snapshots: - return _build_result(address, _conservative_pick(snapshots), corroborated=False) - - return Result( - item=address, - status=ResultStatus.ERROR, - error=str(last_error) if last_error else "Unknown error", - ) - - -class _EvmSignals(QObject): - # _EvmSignals создаётся в __init__ (main thread) — никогда не создавать в run()! - # Это обеспечивает Qt thread affinity для корректной queued-доставки из worker thread. - run_complete = Signal(list, dict) - - -class EvmBalanceCheckerModule(BaseModule): - name = "EVM Balance" - - def column_schema(self) -> list[ColumnDef]: - return [ - ColumnDef(key="item", label="Address", width=200), - ColumnDef(key="status", label="Status"), - ColumnDef(key="total_usd", label="Total $", fmt="${:.2f}", sort_type="numeric"), - ColumnDef(key="native_usd", label="Native $", fmt="${:.2f}", sort_type="numeric"), - ColumnDef(key="verified", label="✓", width=40), - ColumnDef(key="top_tokens", label="Top Tokens"), - ColumnDef(key="active_chains", label="Chains"), - ColumnDef(key="top_chain_usd", label="Top Chain"), - ] - - def __init__(self) -> None: - from app.ui.module_views.evm_balance_view import EvmBalanceConfigWidget - self._signals = _EvmSignals() - self._results: list[Result] = [] - self._details: dict[str, dict] = {} - self._widget = EvmBalanceConfigWidget() - self._signals.run_complete.connect(self._widget.on_run_complete) - self._stop_event = threading.Event() - - def get_config_widget(self): - return self._widget - - def get_item_count(self) -> int: - """Для прогресс-бара MainWindow.""" - return len(self._widget.get_wallets()) - - def get_results(self) -> list[Result]: - return list(self._results) - - async def run(self, ctx: RunContext) -> AsyncIterator[Result]: - self._results.clear() - self._details.clear() - self._stop_event.clear() - - wallets = self._widget.get_wallets() - proxies = self._widget.get_proxies() - rotator = ProxyRotator(proxies) - semaphore = asyncio.Semaphore(ctx.concurrency) - loop = asyncio.get_running_loop() - - async def _indexed_check(idx: int, addr: str) -> tuple[int, Result]: - async with semaphore: - result = await loop.run_in_executor( - None, _check_wallet_sync, addr, rotator, self._stop_event - ) - return idx, result - - tasks = [asyncio.create_task(_indexed_check(i, addr)) for i, addr in enumerate(wallets)] - buffer: dict[int, Result] = {} - next_idx = 0 - try: - for fut in asyncio.as_completed(tasks): - if self._stop_event.is_set(): - for t in tasks: - t.cancel() - break - idx, result = await fut - buffer[idx] = result - while next_idx in buffer: - r = buffer.pop(next_idx) - detail = r.data.pop("_detail", {}) - self._results.append(r) - self._details[r.item] = detail - yield r - next_idx += 1 - finally: - self._signals.run_complete.emit(list(self._results), dict(self._details)) - - async def stop(self) -> None: - self._stop_event.set() diff --git a/app/modules/proxy_checker/__init__.py b/app/modules/proxy_checker/__init__.py deleted file mode 100644 index 7d3bd6c..0000000 --- a/app/modules/proxy_checker/__init__.py +++ /dev/null @@ -1,70 +0,0 @@ -from __future__ import annotations -import asyncio -import threading -from typing import AsyncIterator - -from app.core.base_module import BaseModule -from app.core.models import RunContext, Result, ResultStatus, ProxyConfig, ColumnDef -from app.integrations.pixelscan_client import check_quality - -BATCH_SIZE = 50 - - -async def _check_proxy_async(proxy: ProxyConfig, stop_event: threading.Event) -> Result: - if stop_event.is_set(): - return Result(item=proxy.to_url(), status=ResultStatus.ERROR, error="Stopped") - try: - data = await check_quality(proxy) - data["proxy_type"] = proxy.protocol.upper() - return Result(item=proxy.to_url(), status=ResultStatus.OK, data=data) - except Exception as e: - return Result(item=proxy.to_url(), status=ResultStatus.ERROR, error=str(e)) - - -class ProxyCheckerModule(BaseModule): - name = "Proxy Check" - - def column_schema(self) -> list[ColumnDef]: - return [ - ColumnDef(key="item", label="Proxy", width=220), - ColumnDef(key="status", label="Status"), - ColumnDef(key="proxy_type", label="Type"), - ColumnDef(key="quality", label="Quality"), - ColumnDef(key="latency_ms", label="Latency", fmt="{} ms", sort_type="numeric"), - ] - - def __init__(self) -> None: - from app.ui.module_views.proxy_checker_view import ProxyCheckerConfigWidget - self._widget = ProxyCheckerConfigWidget() - self._stop_event = threading.Event() - - def get_config_widget(self): - return self._widget - - async def run(self, ctx: RunContext) -> AsyncIterator[Result]: - self._stop_event.clear() - proxies = self._widget.get_proxies() - logger = ctx.extra.get("logger") - total = len(proxies) - - for i in range(0, total, BATCH_SIZE): - if self._stop_event.is_set(): - break - batch = proxies[i : i + BATCH_SIZE] - batch_num = i // BATCH_SIZE + 1 - total_batches = (total + BATCH_SIZE - 1) // BATCH_SIZE - if logger: - logger.info( - f"Batch {batch_num}/{total_batches} — checking {len(batch)} proxies ({i + 1}–{i + len(batch)} of {total})…" - ) - results = await asyncio.gather( - *[_check_proxy_async(p, self._stop_event) for p in batch] - ) - for result in results: - yield result - - async def stop(self) -> None: - self._stop_event.set() - - def get_item_count(self) -> int: - return len(self._widget.get_proxies()) diff --git a/app/modules/svm_balance_checker/__init__.py b/app/modules/svm_balance_checker/__init__.py deleted file mode 100644 index f904223..0000000 --- a/app/modules/svm_balance_checker/__init__.py +++ /dev/null @@ -1,142 +0,0 @@ -from __future__ import annotations -import asyncio -import threading -from typing import AsyncIterator - -from PySide6.QtCore import QObject, Signal - -from app.core.base_module import BaseModule -from app.core.models import RunContext, Result, ResultStatus, ColumnDef -from app.integrations.proxy_utils import ProxyRotator -from app.integrations.solana_rpc import SolanaClient - -RETRY_ATTEMPTS = 3 -MIN_VALUE_DISPLAY = 0.01 - - -def _check_wallet_sync( - address: str, - rotator: ProxyRotator, - rpc_url: str, - stop_event: threading.Event, -) -> Result: - """Sync worker — called via run_in_executor. rpc_url is a parameter, not a closure.""" - last_error: Exception | None = None - - for _ in range(RETRY_ATTEMPTS): - if stop_event.is_set(): - return Result(item=address, status=ResultStatus.ERROR, error="Stopped") - - proxy = rotator.next() - if proxy is None: - return Result(item=address, status=ResultStatus.ERROR, error="Нет доступных прокси") - - try: - data = SolanaClient(rpc_url, proxy.to_url()).get_wallet_data(address) - - tokens_data = [t for t in data.tokens if t["value"] >= MIN_VALUE_DISPLAY] - tokens_data.sort(key=lambda x: x["value"], reverse=True) - top_tokens = ", ".join( - f"{t['symbol']}(${t['value']:.2f})" for t in tokens_data[:3] - ) - - return Result( - item=address, - status=ResultStatus.OK, - data={ - "sol_balance": round(data.sol_balance, 6), - "total_usd": round(data.total_usd, 2), - "top_tokens": top_tokens, - "_detail": {"tokens_data": tokens_data}, - }, - ) - except Exception as e: - last_error = e - - return Result( - item=address, - status=ResultStatus.ERROR, - error=str(last_error) if last_error else "Unknown error", - ) - - -class _SvmSignals(QObject): - # Must be instantiated in __init__ on the main thread — never inside run()! - # This ensures correct Qt thread affinity for queued signal delivery from worker threads. - run_complete = Signal(list, dict) - - -class SvmBalanceCheckerModule(BaseModule): - name = "SVM Balance" - - def column_schema(self) -> list[ColumnDef]: - return [ - ColumnDef(key="item", label="Address", width=200), - ColumnDef(key="status", label="Status"), - ColumnDef(key="sol_balance", label="SOL", fmt="{:.4f}", sort_type="numeric"), - ColumnDef(key="total_usd", label="Total $", fmt="${:.2f}", sort_type="numeric"), - ColumnDef(key="top_tokens", label="Top Tokens"), - ] - - def __init__(self) -> None: - from app.ui.module_views.svm_balance_view import SvmBalanceConfigWidget - self._signals = _SvmSignals() - self._results: list[Result] = [] - self._details: dict[str, dict] = {} - self._widget = SvmBalanceConfigWidget() - self._signals.run_complete.connect(self._widget.on_run_complete) - self._stop_event = threading.Event() - - def get_config_widget(self): - return self._widget - - def get_item_count(self) -> int: - return len(self._widget.get_wallets()) - - def get_results(self) -> list[Result]: - return list(self._results) - - async def run(self, ctx: RunContext) -> AsyncIterator[Result]: - self._results.clear() - self._details.clear() - self._stop_event.clear() - - wallets = self._widget.get_wallets() - proxies = self._widget.get_proxies() - rpc_url = self._widget.get_rpc_url() - # ctx.rpc_urls is intentionally ignored — RPC URL is widget-sourced - rotator = ProxyRotator(proxies) - semaphore = asyncio.Semaphore(ctx.concurrency) - loop = asyncio.get_running_loop() - - async def _indexed_check(idx: int, addr: str) -> tuple[int, Result]: - async with semaphore: - result = await loop.run_in_executor( - None, _check_wallet_sync, addr, rotator, rpc_url, self._stop_event - ) - return idx, result - - tasks = [asyncio.create_task(_indexed_check(i, addr)) for i, addr in enumerate(wallets)] - buffer: dict[int, Result] = {} - next_idx = 0 - - try: - for fut in asyncio.as_completed(tasks): - if self._stop_event.is_set(): - for t in tasks: - t.cancel() - break - idx, result = await fut - buffer[idx] = result - while next_idx in buffer: - r = buffer.pop(next_idx) - detail = r.data.pop("_detail", {}) - self._results.append(r) - self._details[r.item] = detail - yield r - next_idx += 1 - finally: - self._signals.run_complete.emit(list(self._results), dict(self._details)) - - async def stop(self) -> None: - self._stop_event.set() diff --git a/app/modules/token_collector/__init__.py b/app/modules/token_collector/__init__.py deleted file mode 100644 index fa5d0e2..0000000 --- a/app/modules/token_collector/__init__.py +++ /dev/null @@ -1,546 +0,0 @@ -# app/modules/token_collector/__init__.py -from __future__ import annotations -import asyncio -import logging -import random -import threading -from typing import AsyncIterator - -from PySide6.QtCore import QObject, Signal - -from app.core.base_module import BaseModule -from app.core.models import RunContext, Result, ResultStatus, ColumnDef -from app.integrations.debank_client import DeBankClient -from app.integrations.lifi_client import ( - LiFiClient, DEBANK_TO_CHAIN_ID, LiFiChainRegistry, -) -from app.integrations.relay_client import RelayClient -from app.integrations.rpc_resolver import RpcResolver -from app.integrations.proxy_utils import ProxyRotator -from app.integrations.chainlist_client import fetch_chainlist_rpcs -from app.modules.token_collector._collector_logic import fetch_and_swap, retry_gasless_swaps -from app.modules.token_collector._bridge_logic import bridge_native, send_to_exchange, refuel_chain - -logger = logging.getLogger(__name__) - - -def _build_processed_bridge_keys(chains_processed: str, target_chains: list[str]) -> list[str]: - """Готовит список source chains для bridge loop без дублей и target chains.""" - target_key_set = {chain.lower() for chain in target_chains} - seen: set[str] = set() - result: list[str] = [] - - for raw_key in chains_processed.split(", "): - key = raw_key.strip() - key_lower = key.lower() - if not key or key_lower in target_key_set or key_lower in seen: - continue - seen.add(key_lower) - result.append(key) - - return result - - -class _CollectorSignals(QObject): - # Создаётся в __init__ (main thread) — обеспечивает Qt thread affinity - run_complete = Signal(list, dict) - - -class CollectorModule(BaseModule): - name = "Collector" - - def column_schema(self) -> list[ColumnDef]: - return [ - ColumnDef(key="item", label="Address", width=200), - ColumnDef(key="status", label="Status"), - ColumnDef(key="tokens_swapped", label="Swaps", sort_type="numeric"), - ColumnDef(key="total_collected_usd", label="Collected $", fmt="${:.2f}", sort_type="numeric"), - ColumnDef(key="bridge_summary", label="Bridges"), - ColumnDef(key="bridge_chain", label="Route"), - ColumnDef(key="bridge_status", label="Bridge"), - ] - - def __init__(self) -> None: - from app.ui.module_views.collector_view import CollectorConfigWidget - self._signals = _CollectorSignals() - self._results: list[Result] = [] - self._widget = CollectorConfigWidget() - self._signals.run_complete.connect(self._widget.on_run_complete) - self._stop_event = threading.Event() - - def get_config_widget(self): - return self._widget - - def get_item_count(self) -> int: - return len(self._widget.get_wallets()) - - async def run(self, ctx: RunContext) -> AsyncIterator[Result]: - self._results.clear() - self._stop_event.clear() - - wallets = self._widget.get_wallets() - proxies = self._widget.get_proxies() - subaccounts = self._widget.get_subaccounts() - settings = self._widget.get_settings() - rotator = ProxyRotator(proxies) - loop = asyncio.get_running_loop() - - # ── ШАГ 0: Валидация ───────────────────────────────────────────── - if settings.send_to_exchange and len(wallets) != len(subaccounts): - raise ValueError( - f"Количество кошельков ({len(wallets)}) не совпадает " - f"с количеством субаккаунтов ({len(subaccounts)})" - ) - - proxy = rotator.next() - proxy_url = proxy.to_url() if proxy else None - lifi_client = LiFiClient(proxy=proxy_url) - relay_client = RelayClient(proxy=proxy_url) - - # LI.FI /chains - lifi_chain_ids: set[int] = set() - lifi_rpc_by_id: dict[int, list[str]] = {} - native_token_by_id: dict[int, dict] = {} - lifi_key_by_id: dict[int, str] = {} - name_by_id: dict[int, str] = {} - - try: - chains = await loop.run_in_executor(None, lifi_client.get_chains) - lifi_chain_ids = {c["id"] for c in chains} - lifi_rpc_by_id = {c["id"]: c.get("metamask", {}).get("rpcUrls", []) for c in chains} - native_token_by_id = {c["id"]: c["nativeToken"] for c in chains if "nativeToken" in c} - lifi_key_by_id = {c["id"]: c.get("key", "") for c in chains} - name_by_id = {c["id"]: c.get("name", str(c["id"])) for c in chains} - logger.info("LI.FI: %d EVM chains loaded", len(lifi_chain_ids)) - except Exception as e: - logger.warning("WARNING: LI.FI /chains failed, using hardcoded chain list. Swaps may be unavailable. (%s)", e) - lifi_chain_ids = set(DEBANK_TO_CHAIN_ID.values()) - lifi_rpc_by_id = {} - native_token_by_id = {} - - # Relay /chains - relay_chain_ids: set[int] = set() - relay_rpc_by_id: dict[int, str] = {} - relay_native_by_id: dict[int, dict] = {} - - try: - relay_chains = await loop.run_in_executor(None, relay_client.get_chains) - logger.info("Relay: %d chains raw from API", len(relay_chains)) - relay_chain_ids = { - c["id"] for c in relay_chains - if c.get("vmType", "evm") == "evm" - and not c.get("disabled", False) - and c.get("depositEnabled", True) - } - relay_rpc_by_id = { - c["id"]: c["httpRpcUrl"] - for c in relay_chains if c.get("httpRpcUrl") - } - relay_native_by_id = {c["id"]: c.get("currency", {}) for c in relay_chains} - logger.info("Relay: %d EVM chains loaded", len(relay_chain_ids)) - except Exception as e: - logger.warning("WARNING: Relay /chains failed, continuing without Relay provider. (%s)", e) - - supported_chain_ids = lifi_chain_ids | relay_chain_ids - if not supported_chain_ids: - raise RuntimeError("No bridge providers available") - - # ChainList RPCs — fallback третий источник (некритично) - chainlist_rpcs: dict[int, list[str]] = {} - try: - chainlist_rpcs = await loop.run_in_executor(None, fetch_chainlist_rpcs) - except Exception as e: - logger.warning("ChainList RPCs unavailable, continuing without: %s", e) - - rpc_resolver = RpcResolver(lifi_rpc_by_id, relay_rpc_by_id, chainlist_rpcs) - - # LI.FI /tools (некритично) - try: - tools = await loop.run_in_executor(None, lifi_client.get_tools) - bridges = tools.get("bridges", []) - exchanges = tools.get("exchanges", []) - logger.info("LI.FI: %d bridges, %d exchanges loaded", len(bridges), len(exchanges)) - except Exception: - pass - - # LI.FI /gas/prices - gas_prices: dict = {} - try: - gas_prices = await loop.run_in_executor(None, lifi_client.get_gas_prices) - except Exception as e: - logger.warning("WARNING: LI.FI /gas/prices failed, will use web3.eth.gas_price. (%s)", e) - - # Резолвинг target_chains → chain IDs - ui_logger = ctx.extra.get("logger") - target_chain_ids: list[int] = [] - for tc in settings.target_chains: - chain_id = DEBANK_TO_CHAIN_ID.get(tc.lower()) - if chain_id is None: - # Пробуем по имени сети (case-insensitive) - for cid, cname in name_by_id.items(): - if cname.lower() == tc.lower(): - chain_id = cid - break - if chain_id and chain_id in supported_chain_ids: - target_chain_ids.append(chain_id) - else: - msg = ( - f"Target chain '{tc}' not found in DEBANK_TO_CHAIN_ID" - if chain_id is None - else f"Target chain '{tc}' (id={chain_id}) not supported by LI.FI or Relay" - ) - logger.warning(msg) - if ui_logger: - ui_logger.warning(f"WARNING: {msg}") - - if not target_chain_ids: - if not settings.target_chains: - raise ValueError("No target chains selected — please pick at least one chain in Bridge settings") - raise ValueError( - f"No valid target chains configured. Selected: {settings.target_chains}. " - f"None are supported by LI.FI or Relay. Check warnings above." - ) - - # ── ШАГ 1–4: обработка кошельков последовательно ───────────────── - logger.info("Starting Collector: %d wallets, target chains: %s", len(wallets), settings.target_chains) - try: - for wallet_idx, wallet in enumerate(wallets): - if self._stop_event.is_set(): - break - - logger.info( - "── Wallet %d/%d ──────────────────────────────", - wallet_idx + 1, len(wallets), - ) - - proxy = rotator.next() - proxy_url = proxy.to_url() if proxy else None - debank_client = DeBankClient(proxy=proxy_url or "http://127.0.0.1:8080") - - result_data: dict = {} - bridge_tx: str | None = None - bridge_ops: list[dict] = [] - bridge_status = "" - exchange_tx: str | None = None - total_sent_usd = 0.0 - - try: - # ШАГ 1-2: DeBank + swap - swap_result = await fetch_and_swap( - wallet=wallet, - lifi_client=lifi_client, - debank_client=debank_client, - rpc_resolver=rpc_resolver, - settings=settings, - native_token_by_id=native_token_by_id, - relay_native_by_id=relay_native_by_id, - lifi_chain_ids=lifi_chain_ids, - supported_chain_ids=supported_chain_ids, - stop_event=self._stop_event, - target_chain_ids=set(target_chain_ids), - ) - - if not swap_result: - result = Result( - item=wallet["raw"][:42], - status=ResultStatus.ERROR, - error="fetch_and_swap failed", - ) - self._results.append(result) - yield result - continue - - address = swap_result["address"] - private_key = swap_result["private_key"] - - # ── ШАГ 2.5–2.6: рефьюел и повторный своп на gasless цепях ── - gasless_chains = swap_result.pop("gasless_chains", []) - if gasless_chains and not self._stop_event.is_set(): - target_key_set_local = set(settings.target_chains) - # Кандидаты-доноры: обработанные не-target цепи + сами target-цепи - donor_candidates: list[int] = [] - for k in swap_result.get("chains_processed", "").split(", "): - if not k or k in target_key_set_local: - continue - cid = DEBANK_TO_CHAIN_ID.get(k) - if cid and cid in relay_chain_ids: - donor_candidates.append(cid) - # Target-цепи часто имеют больше всего нативки (например Soneium с ETH) - for tc_key in target_key_set_local: - tc_cid = DEBANK_TO_CHAIN_ID.get(tc_key.lower()) - if tc_cid and tc_cid in relay_chain_ids and tc_cid not in donor_candidates: - donor_candidates.append(tc_cid) - - refueled: list[dict] = [] - for gc in gasless_chains: - if self._stop_event.is_set(): - break - tgt_cid: int = gc["chain_id"] - if tgt_cid not in relay_chain_ids: - logger.info( - "[Wallet %s] Cannot refuel chain %s: not supported by Relay", - address[:10], tgt_cid, - ) - continue - - # Считаем дефицит газа в USD — нативные токены у разных цепей имеют разную цену - gasless_price_usd = float( - native_token_by_id.get(tgt_cid, {}).get("priceUSD") - or relay_native_by_id.get(tgt_cid, {}).get("priceUSD") - or 2000 - ) - gas_deficit = max(0, gc["max_gas_needed_wei"] - gc.get("eth_balance", 0)) - if gas_deficit <= 0: - logger.info( - "[Wallet %s] Chain %s no longer needs refuel", - address[:10], tgt_cid, - ) - continue - desired_out_wei = gas_deficit * 2 # 2× запас на целевой сети - refuel_usd = desired_out_wei / 1e18 * gasless_price_usd - - donor_found = False - for donor_cid in donor_candidates: - try: - w3_d = rpc_resolver.get_web3(donor_cid) - donor_bal = await loop.run_in_executor( - None, w3_d.eth.get_balance, address - ) - except Exception: - continue - # Считаем USD-баланс донора по цене его нативного токена - donor_price_usd = float( - native_token_by_id.get(donor_cid, {}).get("priceUSD") - or relay_native_by_id.get(donor_cid, {}).get("priceUSD") - or 1 - ) - donor_bal_usd = donor_bal / 1e18 * donor_price_usd - # Донор покрывает рефьюел + $0.01 на газ бридж-транзакции донора - if donor_bal_usd < refuel_usd + 0.01: - continue - # Конвертируем нужную сумму в нативные единицы донора - refuel_amount = int(refuel_usd / donor_price_usd * 1e18) - max_refuel_amount = int(max(0.0, donor_bal_usd - 0.01) / donor_price_usd * 1e18) - if max_refuel_amount <= 0: - continue - ok = await refuel_chain( - address=address, - private_key=private_key, - donor_chain_id=donor_cid, - tgt_chain_id=tgt_cid, - refuel_amount_wei=refuel_amount, - min_required_out_wei=desired_out_wei, - max_refuel_amount_wei=max_refuel_amount, - relay_client=relay_client, - relay_native_by_id=relay_native_by_id, - rpc_resolver=rpc_resolver, - stop_event=self._stop_event, - ) - if ok: - refueled.append(gc) - donor_found = True - break - if not donor_found: - logger.info( - "[Wallet %s] No suitable donor to refuel chain %s", - address[:10], tgt_cid, - ) - - if refueled and not self._stop_event.is_set(): - retry_result = await retry_gasless_swaps( - gasless_chains=refueled, - address=address, - private_key=private_key, - lifi_client=lifi_client, - rpc_resolver=rpc_resolver, - settings=settings, - native_token_by_id=native_token_by_id, - relay_native_by_id=relay_native_by_id, - lifi_chain_ids=lifi_chain_ids, - stop_event=self._stop_event, - ) - if retry_result and retry_result.get("chains_processed"): - existing = swap_result.get("chains_processed", "") - new_processed = retry_result["chains_processed"] - swap_result["chains_processed"] = ( - existing + ", " + new_processed if existing else new_processed - ) - swap_result["tokens_swapped"] = ( - swap_result.get("tokens_swapped", 0) + retry_result.get("tokens_swapped", 0) - ) - swap_result["total_collected_usd"] = round( - swap_result.get("total_collected_usd", 0.0) + retry_result.get("total_usd", 0.0), 2 - ) - - result_data.update(swap_result) - # Удаляем поля избыточные для таблицы - for _k in ("private_key", "address", "chains_processed", "chains_skipped"): - result_data.pop(_k, None) - - processed_keys = _build_processed_bridge_keys( - chains_processed=swap_result.get("chains_processed", ""), - target_chains=settings.target_chains, - ) - if not processed_keys: - result = Result( - item=address, - status=ResultStatus.SKIP, - data=result_data, - ) - self._results.append(result) - yield result - # Посекундная задержка перед следующим кошельком - if wallet_idx < len(wallets) - 1: - delay = random.randint(settings.delay_min, settings.delay_max) - for _ in range(delay): - if self._stop_event.is_set(): - break - await asyncio.sleep(1) - continue - - # Бридж из каждой активной сети - # Приоритет статусов: чем меньше — тем лучше - _BRIDGE_PRIORITY = { - "COMPLETED": 0, "SENT": 1, "PARTIAL": 2, "REFUNDED": 3, - "NODE_REJECTED": 4, "TX_REVERTED": 5, "NO_RPC": 6, "TIMEOUT": 7, - "NO_QUOTE": 8, "INSUFFICIENT": 9, "BELOW_MIN": 10, "NO_ROUTE": 11, "STOPPED": 12, - } - tgt_id = None - best_src_key = "?" - for src_key in processed_keys: - if self._stop_event.is_set(): - break - src_chain_id = DEBANK_TO_CHAIN_ID.get(src_key) - if not src_chain_id: - continue - - # ШАГ 3: бридж - b_tx, b_status, b_tgt_id, b_sent_usd = await bridge_native( - address=address, - private_key=private_key, - lifi_client=lifi_client, - relay_client=relay_client, - rpc_resolver=rpc_resolver, - settings=settings, - lifi_chain_ids=lifi_chain_ids, - relay_chain_ids=relay_chain_ids, - native_token_by_id=native_token_by_id, - relay_native_by_id=relay_native_by_id, - target_chain_ids=target_chain_ids, - src_chain_id=src_chain_id, - gas_prices=gas_prices, - stop_event=self._stop_event, - ) - total_sent_usd += b_sent_usd - - bridge_ops.append({ - "src": src_key, - "tgt": name_by_id.get(b_tgt_id, str(b_tgt_id)) if b_tgt_id else "", - "tx": b_tx or "", - "status": b_status, - "usd": round(b_sent_usd, 4), - }) - - # Сохраняем лучший статус — не даём плохому перезаписать хороший - if _BRIDGE_PRIORITY.get(b_status, 99) < _BRIDGE_PRIORITY.get(bridge_status, 99): - bridge_tx = b_tx - bridge_status = b_status - tgt_id = b_tgt_id - best_src_key = src_key - - # Задержка после каждого выполненного бриджа - if b_status in ("COMPLETED", "SENT") and not self._stop_event.is_set(): - delay = random.randint(settings.delay_min, settings.delay_max) - logger.info( - "[Wallet %s] Delay %ds after bridge from %s (status=%s)", - address[:10], delay, src_key, b_status, - ) - for _ in range(delay): - if self._stop_event.is_set(): - break - await asyncio.sleep(1) - - # ШАГ 4: отправка на биржу — один раз после всех бриджей - if ( - settings.send_to_exchange - and bridge_status in ("COMPLETED", "PARTIAL", "REFUNDED") - and tgt_id is not None - and not self._stop_event.is_set() - ): - exchange_tx = await send_to_exchange( - address=address, - private_key=private_key, - exchange_address=subaccounts[wallet_idx], - tgt_chain_id=tgt_id, - rpc_resolver=rpc_resolver, - gas_prices=gas_prices, - settings=settings, - stop_event=self._stop_event, - ) - - # total_collected_usd = сумма фактически отправленных через бридж средств - result_data["total_collected_usd"] = round(total_sent_usd, 2) - - if tgt_id is not None: - last_tgt = name_by_id.get(tgt_id, str(tgt_id)) - result_data["bridge_chain"] = f"{best_src_key} → {last_tgt}" - else: - result_data["bridge_chain"] = "" - result_data["bridge_tx"] = bridge_tx or "" - # Compact bridge summary for the table column - if isinstance(bridge_ops, list) and bridge_ops: - ok_count = sum(1 for op in bridge_ops if op.get("status") == "ok") - result_data["bridge_summary"] = f"{ok_count}/{len(bridge_ops)}" - else: - result_data["bridge_summary"] = "" - result_data["_detail_bridge_ops"] = bridge_ops # full data for export only - result_data["bridge_status"] = bridge_status - result_data["exchange_tx"] = exchange_tx or "" - _SKIP_STATUSES = ("NO_QUOTE", "NO_ROUTE", "BELOW_MIN", "STOPPED", "INSUFFICIENT", "NOT_INDEXED", "TX_REVERTED") - - if bridge_status in ("COMPLETED", "PARTIAL", "SENT"): - result_status = ResultStatus.OK - elif bridge_status in _SKIP_STATUSES: - result_status = ResultStatus.SKIP - else: - result_status = ResultStatus.ERROR - - logger.info( - "[Wallet %s] Result: %s (bridge_status=%s)", - address, result_status.name, bridge_status, - ) - result = Result( - item=address, - status=result_status, - data=result_data, - ) - - except Exception as e: - logger.exception("[Wallet %d] Unexpected error: %s", wallet_idx, e) - result = Result( - item=wallet.get("raw", "?")[:42], - status=ResultStatus.ERROR, - error=str(e), - ) - - self._results.append(result) - yield result - - # Задержка между кошельками - if wallet_idx < len(wallets) - 1: - delay = random.randint(settings.delay_min, settings.delay_max) - logger.info( - "[Wallet %d] Delay %ds before next wallet...", - wallet_idx + 1, delay, - ) - for _ in range(delay): - if self._stop_event.is_set(): - break - await asyncio.sleep(1) - - finally: - logger.info("Collector finished. Total: %d results.", len(self._results)) - self._signals.run_complete.emit(list(self._results), {}) - - async def stop(self) -> None: - self._stop_event.set() diff --git a/app/modules/token_collector/_bridge_logic.py b/app/modules/token_collector/_bridge_logic.py deleted file mode 100644 index 0bb1397..0000000 --- a/app/modules/token_collector/_bridge_logic.py +++ /dev/null @@ -1,760 +0,0 @@ -# app/modules/token_collector/_bridge_logic.py -from __future__ import annotations -import asyncio -import logging -import random -import threading -import time -from typing import Any - -logger = logging.getLogger(__name__) - -# Таймаут ожидания бриджа — 30 минут -BRIDGE_TIMEOUT_SEC = 30 * 60 - -# OP-stack GasPriceOracle — одинаковый адрес на всех OP-stack цепях -# (OP, Base, INK, Unichain, Mode, Lisk, Soneium, World, Zora...) -_L1_GAS_ORACLE = "0x420000000000000000000000000000000000000F" -_L1_FEE_ABI = [ - { - "inputs": [{"internalType": "bytes", "name": "_data", "type": "bytes"}], - "name": "getL1Fee", - "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}], - "stateMutability": "view", - "type": "function", - } -] - - -def _hi(v) -> int: - """Парсит hex (0x-префикс) или decimal строку/int в int.""" - if not v: - return 0 - if isinstance(v, str): - return int(v, 16) if v.startswith("0x") else int(v) - return int(v) - - -def _tx_gas_cost(tx_req: dict) -> int: - """Реальная стоимость газа из transactionRequest: gasLimit × maxFeePerGas (или gasPrice).""" - - gas_limit = _hi(tx_req.get("gasLimit") or tx_req.get("gas")) - gas_price = _hi(tx_req.get("maxFeePerGas") or tx_req.get("gasPrice")) - return gas_limit * gas_price - - -def _get_l1_fee_safe(w3: Any, tx_req: dict) -> int: - """ - Оценивает L1 data fee для OP-stack цепей через GasPriceOracle.getL1Fee(). - На ETH mainnet, ARB и других non-OP-stack цепях оракул отсутствует → возвращает 0. - - Передаём ~300 байт RLP-заголовка (нули) + calldata — это приближение реального - размера подписанной транзакции. Нули стоят 4 gas/byte (дешевле реального заголовка), - поэтому оценка чуть занижена — умножаем на 1.1 для запаса. - """ - from web3 import Web3 - try: - oracle = w3.eth.contract( - address=Web3.to_checksum_address(_L1_GAS_ORACLE), - abi=_L1_FEE_ABI, - ) - data_hex = tx_req.get("data") or "0x" - calldata = bytes.fromhex(data_hex[2:] if data_hex.startswith("0x") else data_hex) - # ~300 байт RLP-заголовка (нули) + calldata ≈ реальный размер tx - tx_bytes = bytes(300) + calldata - fee = int(oracle.functions.getL1Fee(tx_bytes).call()) - # +10% запас: заголовок содержит ненулевые байты (дороже чем нули в нашей аппроксимации) - return fee * 11 // 10 - except Exception: - return 0 # Non-OP-stack цепь или оракул недоступен - - -def _relay_quote_is_too_small_error(exc: Exception) -> bool: - """Определяет ошибки Relay, где нужно просто увеличить input-amount.""" - msg = str(exc).lower() - needles = ( - "too small", - "cover fees", - "minimum amount", - "minimum input", - "insufficient output amount", - ) - return any(needle in msg for needle in needles) - - -def _relay_quote_out_amount(relay_quote: dict) -> int: - """Возвращает ожидаемый amount на destination chain из Relay quote.""" - try: - return int(relay_quote.get("details", {}).get("currencyOut", {}).get("amount") or 0) - except Exception: - return 0 - - -async def _find_refuel_quote( - *, - loop: asyncio.AbstractEventLoop, - relay_client: Any, - donor_chain_id: int, - tgt_chain_id: int, - donor_native: str, - tgt_native: str, - address: str, - requested_amount_wei: int, - min_required_out_wei: int, - max_refuel_amount_wei: int | None, -) -> tuple[int, int, dict] | tuple[None, None, None]: - """ - Подбирает минимальный input для Relay refuel. - Увеличивает сумму, если route слишком маленький или ожидаемый output меньше требуемого. - """ - candidate_amount = max(1, int(requested_amount_wei)) - max_amount = max(candidate_amount, int(max_refuel_amount_wei or candidate_amount)) - last_error: Exception | None = None - - for _ in range(8): - try: - relay_q = await loop.run_in_executor( - None, - relay_client.get_quote, - donor_chain_id, tgt_chain_id, - donor_native, tgt_native, - str(candidate_amount), address, - ) - out_amount = _relay_quote_out_amount(relay_q) - if min_required_out_wei and out_amount < min_required_out_wei: - last_error = RuntimeError( - f"quoted output {out_amount} < required {min_required_out_wei}" - ) - else: - return candidate_amount, out_amount, relay_q - except Exception as e: - last_error = e - if not _relay_quote_is_too_small_error(e): - break - out_amount = 0 - - if candidate_amount >= max_amount: - break - - if min_required_out_wei and out_amount > 0: - scaled_amount = ( - candidate_amount * min_required_out_wei * 11 + (out_amount * 10) - 1 - ) // (out_amount * 10) - next_amount = max(candidate_amount * 2, scaled_amount) - else: - next_amount = candidate_amount * 2 - - next_amount = min(max_amount, next_amount) - if next_amount <= candidate_amount: - break - - logger.info( - "[Refuel] Bumping input for %s → %s from %d to %d wei", - donor_chain_id, tgt_chain_id, candidate_amount, next_amount, - ) - candidate_amount = next_amount - - if last_error: - logger.warning( - "[Refuel] Could not find viable quote %s → %s up to %d wei: %s", - donor_chain_id, tgt_chain_id, max_amount, last_error, - ) - return None, None, None - - -async def bridge_native( - address: str, - private_key: str, - lifi_client: Any, # LiFiClient - relay_client: Any, # RelayClient - rpc_resolver: Any, # RpcResolver - settings: Any, # CollectorSettings - lifi_chain_ids: set[int], - relay_chain_ids: set[int], - native_token_by_id: dict[int, dict], - relay_native_by_id: dict[int, dict], - target_chain_ids: list[int], - src_chain_id: int, - gas_prices: dict, - stop_event: threading.Event, -) -> tuple[str | None, str, int | None, float]: - """ - ШАГ 3: бридж нативного токена из src_chain_id в target_chain. - Возвращает (bridge_tx, bridge_status, tgt_chain_id, sent_usd). - sent_usd — USD-стоимость отправленной суммы при статусе COMPLETED, иначе 0.0. - """ - from app.integrations.lifi_client import LiFiNoRouteError - from app.integrations.relay_client import RelayNoRouteError - from app.modules.token_collector._signer import sign_and_send, TransactionReverted - - loop = asyncio.get_running_loop() - - tgt_chain_id = random.choice(target_chain_ids) - logger.info("[Wallet %s] Bridge: chain %s → %s", address[:10], src_chain_id, tgt_chain_id) - - # Определяем доступных провайдеров - lifi_ok = src_chain_id in lifi_chain_ids and tgt_chain_id in lifi_chain_ids - relay_ok = src_chain_id in relay_chain_ids and tgt_chain_id in relay_chain_ids - - if not lifi_ok and not relay_ok: - logger.info( - "[Wallet %s] No bridge route %s → %s on any provider", - address[:10], src_chain_id, tgt_chain_id - ) - return None, "NO_ROUTE", None, 0.0 - - # Проверяем LI.FI connections - if lifi_ok: - try: - connections = await loop.run_in_executor( - None, lifi_client.get_connections, src_chain_id, tgt_chain_id - ) - if not connections: - lifi_ok = False - except Exception as e: - logger.warning( - "[Wallet %s] LI.FI connections check failed (%s → %s): %s", - address[:10], src_chain_id, tgt_chain_id, e, - ) - lifi_ok = False - - # Адреса нативных токенов - src_native = ( - native_token_by_id.get(src_chain_id, {}).get("address") - or relay_native_by_id.get(src_chain_id, {}).get("address") - or "0x0000000000000000000000000000000000000000" - ) - tgt_native = ( - native_token_by_id.get(tgt_chain_id, {}).get("address") - or relay_native_by_id.get(tgt_chain_id, {}).get("address") - or "0x0000000000000000000000000000000000000000" - ) - - try: - w3 = rpc_resolver.get_web3(src_chain_id) - except RuntimeError as e: - logger.warning("[Wallet %s] No RPC available for chain %s: %s", address[:10], src_chain_id, e) - return None, "NO_RPC", None, 0.0 - - # Получаем баланс с ротацией при сбое RPC - try: - balance_wei = await loop.run_in_executor(None, w3.eth.get_balance, address) - except Exception as e: - logger.warning("[Wallet %s] RPC error on get_balance chain %s (%s), rotating...", address[:10], src_chain_id, e) - try: - w3 = rpc_resolver.rotate(src_chain_id) - balance_wei = await loop.run_in_executor(None, w3.eth.get_balance, address) - except Exception as e2: - logger.error("[Wallet %s] All RPCs failed for chain %s: %s", address[:10], src_chain_id, e2) - return None, "NO_RPC", None, 0.0 - - # Цена нативного токена (нужна и для min_bridge_usd и для расчёта sent_usd) - price_usd = float( - native_token_by_id.get(src_chain_id, {}).get("priceUSD", 0) - or relay_native_by_id.get(src_chain_id, {}).get("priceUSD", 0) - or 0 - ) - - # Проверка минимальной суммы для бриджа - if settings.min_bridge_usd > 0: - balance_usd = (balance_wei / 1e18) * price_usd - # Допуск 0.001$ чтобы float precision не отсекал пограничные значения ($0.10 vs $0.09999...) - if price_usd > 0 and balance_usd < settings.min_bridge_usd - 0.001: - logger.info( - "[Wallet %s] Bridge skipped: balance $%.4f < min_bridge_usd $%.2f", - address[:10], balance_usd, settings.min_bridge_usd, - ) - return None, "BELOW_MIN", None, 0.0 - - # Пробные котировки для сравнения - lifi_out = 0 - lifi_gas = 0 - lifi_l1_fee = 0 - relay_out = 0 - relay_gas = 0 - relay_l1_fee = 0 - - if lifi_ok: - try: - lifi_q = await loop.run_in_executor( - None, - lifi_client.get_quote, - src_chain_id, tgt_chain_id, - src_native, tgt_native, - balance_wei, address, address, settings.slippage, - ) - # Берём газ из transactionRequest — реальная стоимость source tx (gasLimit × gasPrice). - # gas_suggestion от LI.FI НЕ используем: это "рекомендованный резерв" для многих - # будущих транзакций, он в 5-50x превышает стоимость одного бриджа и блокирует мелкие кошельки. - lifi_tx_req_probe = lifi_q.get("transactionRequest", {}) - lifi_gas = _tx_gas_cost(lifi_tx_req_probe) - if lifi_gas == 0: - lifi_gas = sum(int(g["amount"]) for g in lifi_q.get("estimate", {}).get("gasCosts", [])) - lifi_out = int(lifi_q.get("estimate", {}).get("toAmountMin", 0)) - # L1 data fee для OP-stack (на ETH/ARB/etc. возвращает 0) - lifi_l1_fee = await loop.run_in_executor(None, _get_l1_fee_safe, w3, lifi_tx_req_probe) - except (LiFiNoRouteError, Exception) as e: - logger.warning("[Wallet %s] LI.FI quote failed: %s", address[:10], e) - lifi_ok = False - - if relay_ok: - try: - relay_src_native = ( - relay_native_by_id.get(src_chain_id, {}).get("address") - or "0x0000000000000000000000000000000000000000" - ) - relay_tgt_native = ( - relay_native_by_id.get(tgt_chain_id, {}).get("address") - or "0x0000000000000000000000000000000000000000" - ) - relay_q = await loop.run_in_executor( - None, - relay_client.get_quote, - src_chain_id, tgt_chain_id, - relay_src_native, relay_tgt_native, - str(balance_wei), address, - ) - # Берём газ из tx_req source-транзакции (gasLimit × gasPrice). - # fees["relayer"] — протокольная комиссия, вычитается из VALUE (output), - # не требует дополнительного ETH в кошельке — не включаем в gas_reserve. - relay_tx_req = relay_q["steps"][0]["items"][0]["data"] - relay_gas = _tx_gas_cost(relay_tx_req) - if relay_gas == 0: - relay_gas = int(relay_q["fees"]["gas"]["amount"]) - relay_out = int(relay_q["details"]["currencyOut"]["amount"]) - # L1 data fee для OP-stack (на ETH/ARB/etc. возвращает 0) - relay_l1_fee = await loop.run_in_executor(None, _get_l1_fee_safe, w3, relay_tx_req) - except Exception as e: - logger.warning("[Wallet %s] Relay quote failed: %s", address[:10], e) - relay_ok = False - - if not lifi_ok and not relay_ok: - return None, "NO_QUOTE", None, 0.0 - - # Выбор провайдера по максимальному out - if lifi_ok and relay_ok: - provider = "lifi" if lifi_out >= relay_out else "relay" - elif lifi_ok: - provider = "lifi" - else: - provider = "relay" - - chosen_gas = lifi_gas if provider == "lifi" else relay_gas - chosen_l1_fee = lifi_l1_fee if provider == "lifi" else relay_l1_fee - chosen_out = lifi_out if provider == "lifi" else relay_out - if chosen_l1_fee: - logger.info( - "[Bridge] provider=%s out=%.6f (lifi=%s relay=%s) l1_fee=%d", - provider, chosen_out / 1e18, lifi_out, relay_out, chosen_l1_fee, - ) - else: - logger.info( - "[Bridge] provider=%s out=%.6f (lifi=%s relay=%s)", - provider, chosen_out / 1e18, lifi_out, relay_out, - ) - - # gas_reserve = source tx gas × 2 (запас на случай спайка baseFee) - # + L1 data fee для OP-stack цепей (на ETH/ARB/etc. = 0). - gas_reserve = chosen_gas * 2 + chosen_l1_fee - - if gas_reserve == 0: - # Последний fallback: web3 gas_price × лимит для bridge tx - gas_price_wei = await loop.run_in_executor(None, lambda: w3.eth.gas_price) - gas_reserve = gas_price_wei * 500_000 - logger.info("[Wallet %s] Gas reserve was 0, fallback: %d wei", address[:10], gas_reserve) - - send_amount = balance_wei - gas_reserve - - logger.info( - "[Wallet %s] Gas calc: source_gas=%d reserve=%d balance=%d send=%d ($%.4f)", - address[:10], chosen_gas, gas_reserve, balance_wei, send_amount, - send_amount / 1e18 * float( - native_token_by_id.get(src_chain_id, {}).get("priceUSD", 0) or 0 - ), - ) - - if send_amount <= 0: - logger.info("[Wallet %s] Insufficient balance for bridge after gas reserve", address[:10]) - return None, "INSUFFICIENT", tgt_chain_id, 0.0 - - # Финальная котировка - try: - if provider == "lifi": - final_quote = await loop.run_in_executor( - None, - lifi_client.get_quote, - src_chain_id, tgt_chain_id, - src_native, tgt_native, - send_amount, address, address, settings.slippage, - ) - tx_req = final_quote["transactionRequest"] - else: - relay_src_native = ( - relay_native_by_id.get(src_chain_id, {}).get("address") - or "0x0000000000000000000000000000000000000000" - ) - relay_tgt_native = ( - relay_native_by_id.get(tgt_chain_id, {}).get("address") - or "0x0000000000000000000000000000000000000000" - ) - final_quote = await loop.run_in_executor( - None, - relay_client.get_quote, - src_chain_id, tgt_chain_id, - relay_src_native, relay_tgt_native, - str(send_amount), address, - ) - tx_req = final_quote["steps"][0]["items"][0]["data"] - except Exception as e: - logger.warning( - "[Wallet %s] Final quote failed (provider=%s, send_amount=%d): %s", - address[:10], provider, send_amount, e, - ) - return None, "NO_QUOTE", tgt_chain_id, 0.0 - - # Safety-check: вычисляем gas cost так же как sign_and_send — - # читаем свежий baseFee и применяем priority из котировки. - # ВАЖНО: tx_req.maxFeePerGas у Relay может быть placeholder (0 или 1 wei) — - # нельзя использовать как fallback; нужен реальный baseFee из сети. - gas_limit = _hi(tx_req.get("gasLimit") or tx_req.get("gas")) - base_fee: int | None = None - try: - base_fee = await loop.run_in_executor(None, lambda: w3.eth.get_block("latest")["baseFeePerGas"]) - except Exception as _e1: - logger.debug("[Wallet %s] get_block failed in safety check (%s), trying gas_price...", address[:10], _e1) - try: - base_fee = await loop.run_in_executor(None, lambda: w3.eth.gas_price) - except Exception as _e2: - logger.warning("[Wallet %s] Cannot determine gas price for safety check (%s) — skipping bridge", address[:10], _e2) - return None, "INSUFFICIENT", tgt_chain_id, 0.0 - - if "maxFeePerGas" in tx_req: - priority = max(1, _hi(tx_req.get("maxPriorityFeePerGas"))) - effective_gas_price = base_fee * 11 // 10 + priority - else: - quote_gp = _hi(tx_req.get("gasPrice")) - effective_gas_price = max(quote_gp, base_fee * 2) if base_fee else quote_gp * 2 - - final_gas_cost = gas_limit * effective_gas_price - # L1 data fee для финальной котировки (OP-stack цепи) - l1_data_fee = await loop.run_in_executor(None, _get_l1_fee_safe, w3, tx_req) - tx_value = _hi(tx_req.get("value")) - logger.info( - "[Wallet %s] Safety check: value=%d gas_limit=%d gas_price=%d gas_cost=%d l1_fee=%d balance=%d", - address[:10], tx_value, gas_limit, effective_gas_price, final_gas_cost, l1_data_fee, balance_wei, - ) - if tx_value + final_gas_cost + l1_data_fee > balance_wei: - logger.warning( - "[Wallet %s] Final tx exceeds balance (value=%d gas=%d l1_fee=%d price=%d balance=%d), skipping", - address[:10], tx_value, gas_limit, l1_data_fee, effective_gas_price, balance_wei, - ) - return None, "INSUFFICIENT", tgt_chain_id, 0.0 - - # Снимаем баланс на destination chain ДО отправки tx (Relay доставляет за секунды!) - pre_balance = 0 - try: - w3_tgt = rpc_resolver.get_web3(tgt_chain_id) - pre_balance = await loop.run_in_executor(None, w3_tgt.eth.get_balance, address) - except Exception as e: - logger.warning("[Wallet %s] Could not get pre-bridge balance on chain %s: %s", address[:10], tgt_chain_id, e) - - try: - tx_hash, receipt = await loop.run_in_executor( - None, sign_and_send, w3, tx_req, private_key, address - ) - except TransactionReverted as e: - logger.warning("[Wallet %s] Bridge tx reverted: %s", address[:10], e.tx_hash) - return e.tx_hash, "TX_REVERTED", tgt_chain_id, 0.0 - except Exception as e: - e_str = str(e) - if "-32000" in e_str: - logger.warning("[Wallet %s] Bridge tx rejected by node: %s", address[:10], e_str[:160]) - return None, "NODE_REJECTED", tgt_chain_id, 0.0 - raise - - # Ожидаемый минимум поступления (50% от quoted toAmountMin — консервативный порог) - if provider == "lifi": - to_amount_min = int(final_quote.get("estimate", {}).get("toAmountMin", 0)) - else: - to_amount_min = int(relay_q.get("details", {}).get("currencyOut", {}).get("minimumAmount", 0) or 0) - expected_min = int(to_amount_min * 0.5) if to_amount_min > 0 else 1 - - logger.info( - "[Wallet %s] Bridge tx: %s | pre_balance=%d expected_min=%d wei", - address[:10], tx_hash, pre_balance, expected_min, - ) - - status = await _poll_balance_increase( - rpc_resolver=rpc_resolver, - address=address, - tgt_chain_id=tgt_chain_id, - pre_balance=pre_balance, - expected_min=expected_min, - loop=loop, - stop_event=stop_event, - ) - - # Если tx отправлена, но RPC destination упал — не помечаем как ошибку. - # "SENT" = транзакция в сети, мониторинг недоступен. - if status == "NO_RPC": - logger.warning( - "[Wallet %s] Bridge tx %s sent but destination RPC unavailable — marking SENT", - address[:10], tx_hash, - ) - status = "SENT" - - sent_usd = round(send_amount / 1e18 * price_usd, 4) if status in ("COMPLETED", "SENT") else 0.0 - return tx_hash, status, tgt_chain_id, sent_usd - - -async def _poll_balance_increase( - rpc_resolver: Any, - address: str, - tgt_chain_id: int, - pre_balance: int, - expected_min: int, - loop: asyncio.AbstractEventLoop, - stop_event: threading.Event, -) -> str: - """ - Опрашивает баланс на destination chain каждые 10 сек. - Возвращает COMPLETED когда баланс вырос на expected_min, TIMEOUT через 30 мин. - Надёжнее API-поллинга: не зависит от скорости индексации LI.FI / Relay. - """ - POLL_INTERVAL = 10 - elapsed = 0 - - try: - w3 = rpc_resolver.get_web3(tgt_chain_id) - except Exception as e: - logger.warning("[Bridge] No RPC for destination chain %s: %s", tgt_chain_id, e) - return "NO_RPC" - - while elapsed < BRIDGE_TIMEOUT_SEC: - if stop_event.is_set(): - return "STOPPED" - await asyncio.sleep(POLL_INTERVAL) - elapsed += POLL_INTERVAL - - try: - balance = await loop.run_in_executor(None, w3.eth.get_balance, address) - increase = balance - pre_balance - if increase >= expected_min: - logger.info( - "[Wallet %s] Bridge COMPLETED: balance on chain %s +%.6f ETH (after %ds)", - address[:10], tgt_chain_id, increase / 1e18, elapsed, - ) - return "COMPLETED" - if elapsed % 60 == 0: # лог раз в минуту - logger.info( - "[Wallet %s] Waiting bridge on chain %s... +%.8f ETH / need %.8f ETH (%ds)", - address[:10], tgt_chain_id, - max(increase, 0) / 1e18, expected_min / 1e18, elapsed, - ) - except Exception as e: - logger.warning("[Wallet %s] Balance check error on chain %s: %s — rotating RPC", address[:10], tgt_chain_id, e) - try: - w3 = rpc_resolver.rotate(tgt_chain_id) - except RuntimeError: - logger.error("[Wallet %s] All RPCs exhausted for chain %s", address[:10], tgt_chain_id) - return "NO_RPC" - - logger.error("[Wallet %s] Bridge timeout after %ds", address[:10], BRIDGE_TIMEOUT_SEC) - return "TIMEOUT" - - -async def refuel_chain( - address: str, - private_key: str, - donor_chain_id: int, - tgt_chain_id: int, - refuel_amount_wei: int, - min_required_out_wei: int, - max_refuel_amount_wei: int | None, - relay_client: Any, - relay_native_by_id: dict[int, dict], - rpc_resolver: Any, - stop_event: threading.Event, -) -> bool: - """ - ШАГ 2.5: отправляет небольшое количество нативного токена из donor_chain_id - в tgt_chain_id через Relay, чтобы покрыть газ для последующих свапов. - Возвращает True если средства успешно доставлены. - """ - from app.modules.token_collector._signer import sign_and_send - - REFUEL_TIMEOUT_SEC = 3 * 60 # 3 минуты - POLL_INTERVAL = 10 - - loop = asyncio.get_running_loop() - - donor_native = ( - relay_native_by_id.get(donor_chain_id, {}).get("address") - or "0x0000000000000000000000000000000000000000" - ) - tgt_native = ( - relay_native_by_id.get(tgt_chain_id, {}).get("address") - or "0x0000000000000000000000000000000000000000" - ) - - logger.info( - "[Refuel] %s → chain %s: requesting %d wei (%s ETH) for gas", - donor_chain_id, tgt_chain_id, refuel_amount_wei, refuel_amount_wei / 1e18, - ) - - actual_refuel_amount, quoted_out_amount, relay_q = await _find_refuel_quote( - loop=loop, - relay_client=relay_client, - donor_chain_id=donor_chain_id, - tgt_chain_id=tgt_chain_id, - donor_native=donor_native, - tgt_native=tgt_native, - address=address, - requested_amount_wei=refuel_amount_wei, - min_required_out_wei=min_required_out_wei, - max_refuel_amount_wei=max_refuel_amount_wei, - ) - if not relay_q or actual_refuel_amount is None: - return False - - tx_req = relay_q["steps"][0]["items"][0]["data"] - if actual_refuel_amount != refuel_amount_wei: - logger.info( - "[Refuel] Adjusted input %s → %s: %d -> %d wei (quoted out %d wei)", - donor_chain_id, tgt_chain_id, refuel_amount_wei, actual_refuel_amount, quoted_out_amount or 0, - ) - else: - logger.info( - "[Refuel] %s → chain %s: sending %d wei (%s ETH) for gas", - donor_chain_id, tgt_chain_id, actual_refuel_amount, actual_refuel_amount / 1e18, - ) - - try: - w3_donor = rpc_resolver.get_web3(donor_chain_id) - except Exception as e: - logger.warning("[Refuel] No RPC for donor chain %s: %s", donor_chain_id, e) - return False - - # Снимаем pre_balance на tgt_chain ДО отправки - pre_balance = 0 - try: - w3_tgt = rpc_resolver.get_web3(tgt_chain_id) - pre_balance = await loop.run_in_executor(None, w3_tgt.eth.get_balance, address) - except Exception as e: - logger.warning("[Refuel] Could not get pre-balance on chain %s: %s", tgt_chain_id, e) - - try: - tx_hash, _ = await loop.run_in_executor( - None, sign_and_send, w3_donor, tx_req, private_key, address - ) - except Exception as e: - logger.warning("[Refuel] Failed to send refuel tx: %s", e) - return False - - logger.info("[Refuel] Tx sent: %s — waiting for arrival on chain %s...", tx_hash, tgt_chain_id) - - # Ожидаем поступления - expected_base = quoted_out_amount or actual_refuel_amount - expected_min = max(1, int(expected_base * 0.3)) # 30% от ожидаемого output - elapsed = 0 - while elapsed < REFUEL_TIMEOUT_SEC: - if stop_event.is_set(): - return False - await asyncio.sleep(POLL_INTERVAL) - elapsed += POLL_INTERVAL - try: - balance = await loop.run_in_executor(None, w3_tgt.eth.get_balance, address) - if balance - pre_balance >= expected_min: - logger.info( - "[Refuel] Arrived on chain %s: +%.8f ETH (after %ds)", - tgt_chain_id, (balance - pre_balance) / 1e18, elapsed, - ) - return True - except Exception as e: - logger.warning("[Refuel] Balance check error on chain %s: %s", tgt_chain_id, e) - try: - w3_tgt = rpc_resolver.rotate(tgt_chain_id) - except RuntimeError: - return False - - logger.warning("[Refuel] Timeout waiting for arrival on chain %s", tgt_chain_id) - return False - - -async def send_to_exchange( - address: str, - private_key: str, - exchange_address: str, - tgt_chain_id: int, - rpc_resolver: Any, - gas_prices: dict, - settings: Any, - stop_event: threading.Event, -) -> str | None: - """ - ШАГ 4: посекундный delay, затем ETH transfer на субаккаунт биржи. - Возвращает tx_hash или None при ошибке/пропуске. - """ - loop = asyncio.get_running_loop() - - logger.info( - "[Wallet %s] Waiting %ds before exchange transfer...", - address[:10], settings.delay_after_bridge - ) - # Посекундный цикл — Stop сработает немедленно - for _ in range(settings.delay_after_bridge): - if stop_event.is_set(): - return None - await asyncio.sleep(1) - - try: - w3 = rpc_resolver.get_web3(tgt_chain_id) - balance_wei = await loop.run_in_executor(None, w3.eth.get_balance, address) - - # Расчёт газа - chain_gas = gas_prices.get(str(tgt_chain_id), gas_prices.get(tgt_chain_id, {})) - gas_price_wei = chain_gas.get("fast") or chain_gas.get("standard") or 0 - if not gas_price_wei: - gas_price_wei = await loop.run_in_executor(None, lambda: w3.eth.gas_price) - - tx_stub = {"from": address, "to": exchange_address, "value": balance_wei} - estimated_gas = await loop.run_in_executor(None, w3.eth.estimate_gas, tx_stub) - gas_cost_wei = estimated_gas * gas_price_wei - send_amount = balance_wei - (gas_cost_wei * 2) - - if send_amount <= 0: - logger.info("[Wallet %s] Balance too low for exchange transfer", address[:10]) - return None - - # Строим Legacy-транзакцию для простого ETH transfer - nonce = await loop.run_in_executor( - None, lambda: w3.eth.get_transaction_count(address, "pending") - ) - chain_id = await loop.run_in_executor(None, lambda: w3.eth.chain_id) - tx_req = { - "to": exchange_address, - "data": "0x", - "value": hex(send_amount), - "gasLimit": hex(estimated_gas), - "gasPrice": hex(gas_price_wei), - "chainId": chain_id, - } - - from app.modules.token_collector._signer import sign_and_send - tx_hash, receipt = await loop.run_in_executor( - None, sign_and_send, w3, tx_req, private_key, address - ) - - if receipt is None: - return None - - native_symbol = "ETH" - logger.info( - "[Wallet %s] Exchange transfer: %.6f %s → %s | tx: %s", - address[:10], send_amount / 1e18, native_symbol, exchange_address, tx_hash - ) - return tx_hash - - except Exception as e: - logger.error("[Wallet %s] Exchange transfer error: %s", address[:10], e) - return None diff --git a/app/modules/token_collector/_collector_logic.py b/app/modules/token_collector/_collector_logic.py deleted file mode 100644 index 6f0217a..0000000 --- a/app/modules/token_collector/_collector_logic.py +++ /dev/null @@ -1,736 +0,0 @@ -# app/modules/token_collector/_collector_logic.py -from __future__ import annotations -import asyncio -import logging -import random -import threading -import time -from decimal import Decimal -from typing import Any - -logger = logging.getLogger(__name__) - - -# ── Anti-phantom corroboration (см. evm_balance_checker) ─────────────────── -# Под высокой параллельной нагрузкой DeBank ~1/30 отдаёт портфель ЧУЖОГО адреса. -# Ответ внутренне согласован (реальные токены), поэтому одиночная выборка фантом -# не ловит. Особенность: истинный набор токенов СТАБИЛЕН между запросами, -# фантом — СЛУЧАЕН и не повторяется. Принимаем список только когда >=2 -# независимые выборки сходятся по суммарному total_usd; иначе — консервативно -# (наименьший total_usd), чтобы не раздувать своп фантомными токенами. ~2 запр./кошелёк. -COLLECTOR_CORROBORATION_MIN_AGREE = 2 # сколько согласных выборок нужно -COLLECTOR_CORROBORATION_TOL_PCT = 0.02 # относительный допуск (2%) -COLLECTOR_CORROBORATION_TOL_ABS = 1.0 # абсолютный допуск ($1) — для мелких балансов -COLLECTOR_CORROBORATION_MAX_FETCHES = 5 # бюджет УСПЕШНЫХ выборок на кошелёк - - -def _tokens_total_usd(tokens: Any) -> float: - """Суммарная USD-стоимость списка токенов DeBank (price * amount).""" - if not isinstance(tokens, list): - return 0.0 - total = 0.0 - for t in tokens: - try: - total += (t.get("price", 0) or 0) * (t.get("amount", 0) or 0) - except (TypeError, ValueError, AttributeError): - continue - return total - - -def _values_agree(a: float, b: float) -> bool: - """Согласованы ли два total_usd в пределах абсолютного ИЛИ относительного допуска.""" - diff = abs(a - b) - if diff <= COLLECTOR_CORROBORATION_TOL_ABS: - return True - return diff <= COLLECTOR_CORROBORATION_TOL_PCT * max(abs(a), abs(b), 1.0) - - -def _agreeing_cluster(snapshots: list[dict]) -> list[dict] | None: - """Наибольший кластер согласных по total_usd выборок (размером >= MIN_AGREE). - - Возвращает None, пока ни один кластер не набрал нужного числа подтверждений. - """ - best: list[dict] | None = None - for anchor in snapshots: - cluster = [s for s in snapshots if _values_agree(s["total_usd"], anchor["total_usd"])] - if len(cluster) >= COLLECTOR_CORROBORATION_MIN_AGREE and (best is None or len(cluster) > len(best)): - best = cluster - return best - - -def _representative(cluster: list[dict]) -> dict: - """Из согласного кластера берём выборку с медианным total_usd (стабильный выбор).""" - ordered = sorted(cluster, key=lambda s: s["total_usd"]) - return ordered[len(ordered) // 2] - - -def _conservative_pick(snapshots: list[dict]) -> dict: - """Бюджет исчерпан без согласия → наименьший total_usd (исключает раздувание фантомом).""" - return min(snapshots, key=lambda s: s["total_usd"]) - - -def _get_native_addr( - chain_id: int, - native_token_by_id: dict[int, dict], - relay_native_by_id: dict[int, dict], -) -> str: - """Возвращает адрес нативного токена сети. Приоритет: LI.FI → Relay → "".""" - addr = ( - native_token_by_id.get(chain_id, {}).get("address") - or relay_native_by_id.get(chain_id, {}).get("address") - or "" - ) - return addr - - -def _resolve_contract(token: dict) -> str: - """ - Возвращает адрес контракта токена. - DeBank API не возвращает поле contract_address — контракт ERC-20 хранится в поле id. - Для нативных токенов id = ключ цепи (не hex), поэтому проверяем startswith("0x"). - """ - token_id = token.get("id", "") - explicit = token.get("contract_address", "") - if explicit: - return explicit.lower() - # Если id выглядит как hex-адрес — это контракт ERC-20 - if token_id.startswith("0x"): - return token_id.lower() - return "" - - -def _is_native_token( - token: dict, - chain_id: int, - native_token_by_id: dict[int, dict], - relay_native_by_id: dict[int, dict], - debank_key: str = "", -) -> bool: - """Определяет является ли токен нативным для данной сети.""" - token_id = token.get("id", "") - contract = _resolve_contract(token) - zero_addr = "0x0000000000000000000000000000000000000000" - - # Условие 1: id совпадает с DeBank chain key (нативный — "eth", "op", "arb"...) - if debank_key and token_id == debank_key: - return True - - # Условие 2: пустой или нулевой адрес контракта — нативный токен - if not contract or contract == zero_addr: - return True - - # Условие 3: адрес из registry (для Celo, RARI и др.) - native_addr = _get_native_addr(chain_id, native_token_by_id, relay_native_by_id) - if native_addr and contract == native_addr.lower(): - return True - - return False - - -def _remember_gasless_chain( - gasless_map: dict[int, dict], - *, - chain_id: int, - debank_key: str, - eth_balance: int, - required_wei: int, - native_token_addr: str, - contract: str, - symbol: str, - value_usd: float, -) -> None: - """Сохраняет цепь как кандидат на refuel и накапливает проблемные токены.""" - if chain_id not in gasless_map: - gasless_map[chain_id] = { - "chain_id": chain_id, - "debank_key": debank_key, - "eth_balance": eth_balance, - "max_gas_needed_wei": 0, - "native_token_addr": native_token_addr, - "tokens": [], - } - - gc = gasless_map[chain_id] - gc["eth_balance"] = min(gc.get("eth_balance", eth_balance), eth_balance) - gc["max_gas_needed_wei"] = max(gc["max_gas_needed_wei"], int(required_wei)) - - known_contracts = {token["contract"] for token in gc["tokens"]} - if contract not in known_contracts: - gc["tokens"].append({"contract": contract, "symbol": symbol, "value_usd": value_usd}) - - -def _hi(v) -> int: - """Парсит hex (0x-префикс) или decimal строку/int в int.""" - if not v: - return 0 - if isinstance(v, str): - return int(v, 16) if v.startswith("0x") else int(v) - return int(v) - - -async def _estimate_swap_tx_cost( - loop: asyncio.AbstractEventLoop, - w3: Any, - tx_req: dict, -) -> tuple[int, int, int, int]: - """ - Возвращает (gas_limit, effective_gas_price, tx_value, l1_data_fee) для swap tx. - Логика зеркалирует sign_and_send и добавляет L1 data fee для OP-stack цепей. - """ - from app.modules.token_collector._bridge_logic import _get_l1_fee_safe - - gas_limit = _hi(tx_req.get("gasLimit") or tx_req.get("gas")) - tx_value = _hi(tx_req.get("value")) - - if "maxFeePerGas" in tx_req: - priority = max(1, _hi(tx_req.get("maxPriorityFeePerGas"))) - try: - base_fee = await loop.run_in_executor( - None, lambda: w3.eth.get_block("latest")["baseFeePerGas"] - ) - effective_gas_price = base_fee * 11 // 10 + priority - except Exception: - quote_max_fee = _hi(tx_req.get("maxFeePerGas")) - effective_gas_price = max(quote_max_fee * 2, priority) - else: - quote_gas_price = _hi(tx_req.get("gasPrice")) - try: - base_fee = await loop.run_in_executor( - None, lambda: w3.eth.get_block("latest").get("baseFeePerGas") or 0 - ) - effective_gas_price = max(quote_gas_price, base_fee * 2) if base_fee else quote_gas_price * 2 - except Exception: - effective_gas_price = quote_gas_price * 2 if quote_gas_price else 0 - - l1_data_fee = await loop.run_in_executor(None, _get_l1_fee_safe, w3, tx_req) - return gas_limit, effective_gas_price, tx_value, l1_data_fee - - -async def fetch_and_swap( - wallet: dict, # {"raw": "0x...", "type": "private_key"} - lifi_client: Any, # LiFiClient - debank_client: Any, # DeBankClient - rpc_resolver: Any, # RpcResolver - settings: Any, # CollectorSettings - native_token_by_id: dict[int, dict], - relay_native_by_id: dict[int, dict], - lifi_chain_ids: set[int], - supported_chain_ids: set[int], - stop_event: threading.Event, - target_chain_ids: set[int] | None = None, # исключаем таргет-цепи из total_usd -) -> dict: - """ - ШАГ 1-2: получить балансы DeBank, отфильтровать, - своп не-нативных токенов в нативный через LI.FI. - Возвращает статистику: chains_processed, chains_skipped, tokens_swapped, total_usd. - """ - from app.integrations.lifi_client import DEBANK_TO_CHAIN_ID, LiFiNoRouteError - from app.modules.token_collector._signer import derive_address, sign_and_send, ensure_erc20_approval, InsufficientFundsError - - address, private_key = derive_address(wallet["raw"], wallet["type"]) - loop = asyncio.get_running_loop() - - # ШАГ 1: Получить балансы с подтверждением против фантомов DeBank. - # Принимаем список токенов только когда >=2 независимые выборки сошлись по - # суммарному total_usd (случайный фантом почти никогда не повторяется). - DEBANK_RETRY = 10 - snapshots: list[dict] = [] # [{"total_usd": float, "tokens": list}] - last_exc: Exception | None = None - attempts = 0 - max_attempts = COLLECTOR_CORROBORATION_MAX_FETCHES + DEBANK_RETRY # запас на сетевые сбои - corroborated = False - tokens: list[dict] = [] - - while attempts < max_attempts and len(snapshots) < COLLECTOR_CORROBORATION_MAX_FETCHES: - if stop_event.is_set(): - return {} - attempts += 1 - try: - snap_tokens = await loop.run_in_executor(None, debank_client.get_tokens, address) - except Exception as e: - last_exc = e - await asyncio.sleep(3) - continue - snapshots.append({ - "total_usd": _tokens_total_usd(snap_tokens), - "tokens": snap_tokens if isinstance(snap_tokens, list) else [], - }) - cluster = _agreeing_cluster(snapshots) - if cluster is not None: - tokens = _representative(cluster)["tokens"] - corroborated = True - break - - if not corroborated: - if not snapshots: - logger.error("[%s] DeBank failed after %d attempts: %s", address[:10], attempts, last_exc) - return {} - tokens = _conservative_pick(snapshots)["tokens"] - logger.warning( - "[Wallet %s] DeBank balances NOT corroborated after %d fetches " - "(no >=%d agreement on total_usd) — берём консервативный снимок ($%.2f); " - "возможен фантом, своп идёт только по on-chain balanceOf", - address[:10], len(snapshots), COLLECTOR_CORROBORATION_MIN_AGREE, - _tokens_total_usd(tokens), - ) - - # Группируем по chain - chains: dict[str, list[dict]] = {} - for t in tokens: - chains.setdefault(t.get("chain", ""), []).append(t) - - chains_processed: list[str] = [] - chains_skipped: list[str] = [] - tokens_swapped = 0 - total_usd = 0.0 - gasless_map: dict[int, dict] = {} # chain_id → {chain_id, debank_key, eth_balance, max_gas_needed_wei, native_token_addr, tokens} - - all_chain_keys = sorted(chains.keys()) - logger.info( - "[Wallet %s] Fetched %d tokens across %d chains (%s)", - address[:10], len(tokens), len(all_chain_keys), ", ".join(all_chain_keys) - ) - - for debank_key, chain_tokens in chains.items(): - if stop_event.is_set(): - break - - # Фильтр: исключённые сети - if debank_key.lower() in (s.lower() for s in settings.excluded_chains): - chains_skipped.append(f"{debank_key} (excluded)") - logger.info("[Wallet %s] Skipping %s: excluded by user", address[:10], debank_key) - continue - - # Фильтр: нет в маппинге - chain_id = DEBANK_TO_CHAIN_ID.get(debank_key) - if chain_id is None: - chains_skipped.append(f"{debank_key} (no mapping)") - continue - - # Фильтр: нет в supported_chain_ids - if chain_id not in supported_chain_ids: - chains_skipped.append(f"{debank_key} (not supported)") - logger.info("[Wallet %s] Skipping %s: chain not supported by LI.FI", address[:10], debank_key) - continue - - # Определяем нативный адрес для этой сети - native_addr = _get_native_addr(chain_id, native_token_by_id, relay_native_by_id) - if native_addr: - native_token_addr = native_addr - else: - native_token_addr = "0x0000000000000000000000000000000000000000" - - # ШАГ 2: своп не-нативных токенов - chain_native_usd = 0.0 - chain_skipped_dust = 0 - chain_swapped = 0 - - for token in chain_tokens: - if stop_event.is_set(): - break - - symbol = token.get("symbol", "?") - value_usd = token.get("price", 0) * token.get("amount", 0) - - if value_usd < settings.min_token_usd: - logger.info( - "[Wallet %s] [%s] Skip %s: $%.4f < min $%.2f", - address[:10], debank_key, symbol, value_usd, settings.min_token_usd, - ) - chain_skipped_dust += 1 - continue - - if _is_native_token(token, chain_id, native_token_by_id, relay_native_by_id, debank_key): - logger.info( - "[Wallet %s] [%s] Native %s $%.2f — no swap needed", - address[:10], debank_key, symbol, value_usd, - ) - chain_native_usd += value_usd - # Нативный ETH на source-цепях не считаем как "собранный" здесь — - # он будет учтён как sent_usd при успешном бридже. - continue - - contract = _resolve_contract(token) - decimals = token.get("decimals", 18) - - # Получаем w3 и реальный on-chain баланс — DeBank-кэш может быть устаревшим - try: - w3 = rpc_resolver.get_web3(chain_id) - except Exception as e: - logger.warning("[Wallet %s] [%s] No RPC for chain %d, skipping %s: %s", - address[:10], debank_key, chain_id, symbol, e) - continue - - _ERC20_BALANCE_ABI = [{"inputs": [{"name": "account", "type": "address"}], - "name": "balanceOf", "outputs": [{"name": "", "type": "uint256"}], - "type": "function", "stateMutability": "view"}] - - async def _get_balance(w3_inst) -> int: - tc = w3_inst.eth.contract( - address=w3_inst.to_checksum_address(contract), abi=_ERC20_BALANCE_ABI - ) - return await loop.run_in_executor( - None, tc.functions.balanceOf(w3_inst.to_checksum_address(address)).call - ) - - try: - from_amount = await _get_balance(w3) - except Exception as e: - # Пробуем следующий RPC - try: - w3 = rpc_resolver.rotate(chain_id) - from_amount = await _get_balance(w3) - except Exception as e2: - logger.warning("[Wallet %s] [%s] balanceOf failed for %s, using DeBank amount: %s", - address[:10], debank_key, symbol, e2) - amount = token.get("amount", 0) - from_amount = int(Decimal(str(amount)) * Decimal(10 ** decimals)) - - if from_amount == 0: - logger.info("[Wallet %s] [%s] Skip %s: on-chain balance is 0", address[:10], debank_key, symbol) - continue - - logger.info("[Wallet %s] [%s] Swapping %s ($%.2f) → native", address[:10], debank_key, symbol, value_usd) - - gas_limit = 0 - effective_gas_price = 0 - tx_value = 0 - eth_needed = 0 - eth_balance = 0 - l1_data_fee = 0 - spender = "" - approval_done = False - tx_req: dict[str, Any] = {} - APPROVE_GAS_BUFFER = 100_000 - - try: - # Проверяем маршрут - connections = await loop.run_in_executor( - None, lifi_client.get_connections, chain_id, chain_id - ) - if not connections: - logger.info("[Wallet %s] [%s] No swap route for %s on %s", address[:10], debank_key, symbol, debank_key) - continue - - # Получаем котировку - quote = await loop.run_in_executor( - None, - lifi_client.get_quote, - chain_id, chain_id, - contract, native_token_addr, - from_amount, address, - address, settings.slippage, - ) - - tx_req = quote["transactionRequest"] - - gas_limit, effective_gas_price, tx_value, l1_data_fee = await _estimate_swap_tx_cost( - loop, w3, tx_req - ) - # Резерв на отдельный approve tx. На OP-stack учитываем и его L1 data fee. - approve_reserve = APPROVE_GAS_BUFFER * effective_gas_price - if tx_req.get("to"): - approve_reserve += l1_data_fee - eth_needed = tx_value + gas_limit * effective_gas_price + l1_data_fee + approve_reserve - eth_balance = await loop.run_in_executor(None, w3.eth.get_balance, address) - - # +10% запас на флуктуацию baseFee между предпроверкой и отправкой - if eth_balance < int(eth_needed * 1.1): - logger.warning( - "[Wallet %s] [%s] Skip %s swap: ETH balance %d < needed %d " - "(gas_limit=%d gas_price=%d l1_fee=%d approve_buf=%d value=%d)", - address[:10], debank_key, symbol, - eth_balance, eth_needed, - gas_limit, effective_gas_price, - l1_data_fee, - APPROVE_GAS_BUFFER * effective_gas_price, - tx_value, - ) - _remember_gasless_chain( - gasless_map, - chain_id=chain_id, - debank_key=debank_key, - eth_balance=eth_balance, - required_wei=int(eth_needed * 1.1), - native_token_addr=native_token_addr, - contract=contract, - symbol=symbol, - value_usd=value_usd, - ) - continue - - # ERC-20 approve: даём разрешение LI.FI diamond тратить токен - spender = tx_req.get("to", "") - approval_done = not bool(spender) - if spender: - approved = await loop.run_in_executor( - None, ensure_erc20_approval, - w3, contract, address, spender, from_amount, private_key, - ) - if not approved: - logger.error( - "[Wallet %s] [%s] Approve failed for %s, skipping swap", - address[:10], debank_key, symbol, - ) - continue - approval_done = True - - tx_hash, receipt = await loop.run_in_executor( - None, sign_and_send, w3, tx_req, private_key, address - ) - - if receipt is None: - # Таймаут ожидания — транзакция может быть pending, не reverted - logger.warning( - "[Wallet %s] [%s] Swap tx pending (receipt timeout): %s", - address[:10], debank_key, tx_hash, - ) - continue - - logger.info( - "[Wallet %s] [%s] Swap %s → native | tx: %s | confirmed in block %s", - address[:10], debank_key, symbol, tx_hash, receipt.blockNumber - ) - tokens_swapped += 1 - chain_swapped += 1 - total_usd += value_usd - - # Задержка после каждого свапа - delay = random.randint(settings.delay_min, settings.delay_max) - logger.info("[Wallet %s] [%s] Delay %ds after swap", address[:10], debank_key, delay) - for _ in range(delay): - if stop_event.is_set(): - break - await asyncio.sleep(1) - - except InsufficientFundsError: - current_balance = eth_balance - try: - current_balance = await loop.run_in_executor(None, w3.eth.get_balance, address) - except Exception as balance_error: - logger.debug( - "[Wallet %s] [%s] Could not refresh ETH balance after insufficient funds: %s", - address[:10], debank_key, balance_error, - ) - - if tx_req: - gas_limit, effective_gas_price, tx_value, l1_data_fee = await _estimate_swap_tx_cost( - loop, w3, tx_req - ) - - remaining_needed = tx_value + gas_limit * effective_gas_price + l1_data_fee - if approval_done: - required_wei = remaining_needed - else: - approve_reserve = APPROVE_GAS_BUFFER * effective_gas_price - if spender: - approve_reserve += l1_data_fee - required_wei = remaining_needed + approve_reserve - - # Нода уже отвергла tx, значит наша локальная оценка всё ещё занижена - # (например из-за L1 fee или роста baseFee). Делаем гарантированно - # положительный дефицит, чтобы refuel не ушёл в отрицательное значение. - if required_wei <= current_balance: - shortfall_floor = max( - l1_data_fee, - gas_limit * max(effective_gas_price, 1) // 10, - 1, - ) - required_wei = current_balance + shortfall_floor - - if required_wei > 0: - _remember_gasless_chain( - gasless_map, - chain_id=chain_id, - debank_key=debank_key, - eth_balance=current_balance, - required_wei=int(required_wei * 1.1), - native_token_addr=native_token_addr, - contract=contract, - symbol=symbol, - value_usd=value_usd, - ) - logger.warning( - "[Wallet %s] [%s] Skip %s swap: insufficient ETH for gas (node rejected)", - address[:10], debank_key, symbol, - ) - except LiFiNoRouteError as e: - logger.info("[Wallet %s] [%s] No route for %s: %s", address[:10], debank_key, symbol, e) - except Exception as e: - logger.error("[Wallet %s] [%s] Swap error for %s: %s", address[:10], debank_key, symbol, e) - - logger.info( - "[Wallet %s] [%s] Chain summary: native=$%.2f swapped=%d dust_skipped=%d", - address[:10], debank_key, chain_native_usd, chain_swapped, chain_skipped_dust, - ) - chains_processed.append(debank_key) - - return { - "address": address, - "private_key": private_key, - "chains_processed": ", ".join(chains_processed), - "chains_skipped": ", ".join(chains_skipped), - "tokens_swapped": tokens_swapped, - "total_collected_usd": round(total_usd, 2), - "gasless_chains": list(gasless_map.values()), - } - - -async def retry_gasless_swaps( - gasless_chains: list[dict], - address: str, - private_key: str, - lifi_client: Any, - rpc_resolver: Any, - settings: Any, - native_token_by_id: dict[int, dict], - relay_native_by_id: dict[int, dict], - lifi_chain_ids: set[int], - stop_event: threading.Event, -) -> dict: - """ - ШАГ 2.6: повторный своп токенов на цепях, которые были рефьюелены. - Не вызывает DeBank — использует список токенов из gasless_chains. - Возвращает {chains_processed, tokens_swapped, total_usd}. - """ - from app.integrations.lifi_client import DEBANK_TO_CHAIN_ID, LiFiNoRouteError - from app.modules.token_collector._signer import sign_and_send, ensure_erc20_approval, InsufficientFundsError - - loop = asyncio.get_running_loop() - chains_processed: list[str] = [] - tokens_swapped = 0 - total_usd = 0.0 - - _ERC20_BALANCE_ABI = [{"inputs": [{"name": "account", "type": "address"}], - "name": "balanceOf", "outputs": [{"name": "", "type": "uint256"}], - "type": "function", "stateMutability": "view"}] - - for gc in gasless_chains: - if stop_event.is_set(): - break - - chain_id: int = gc["chain_id"] - debank_key: str = gc["debank_key"] - native_token_addr: str = gc["native_token_addr"] - - if chain_id not in lifi_chain_ids: - continue - - try: - w3 = rpc_resolver.get_web3(chain_id) - except Exception as e: - logger.warning("[Retry] No RPC for chain %s: %s", chain_id, e) - continue - - chain_swapped = 0 - - for token_info in gc.get("tokens", []): - if stop_event.is_set(): - break - - contract: str = token_info["contract"] - symbol: str = token_info["symbol"] - value_usd: float = token_info["value_usd"] - - if value_usd < settings.min_token_usd: - continue - - # Актуальный on-chain баланс - try: - tc = w3.eth.contract( - address=w3.to_checksum_address(contract), abi=_ERC20_BALANCE_ABI - ) - from_amount = await loop.run_in_executor( - None, tc.functions.balanceOf(w3.to_checksum_address(address)).call - ) - except Exception as e: - logger.warning("[Retry] [%s] balanceOf failed for %s: %s", debank_key, symbol, e) - continue - - if from_amount == 0: - logger.info("[Retry] [%s] %s on-chain balance is 0, skipping", debank_key, symbol) - continue - - logger.info("[Retry] [%s] Swapping %s ($%.2f) → native after refuel", debank_key, symbol, value_usd) - - try: - connections = await loop.run_in_executor( - None, lifi_client.get_connections, chain_id, chain_id - ) - if not connections: - continue - - quote = await loop.run_in_executor( - None, lifi_client.get_quote, - chain_id, chain_id, - contract, native_token_addr, - from_amount, address, address, settings.slippage, - ) - tx_req = quote["transactionRequest"] - - # Простая проверка газа - def _hi(v) -> int: - if not v: - return 0 - if isinstance(v, str): - return int(v, 16) if v.startswith("0x") else int(v) - return int(v) - - gas_limit = _hi(tx_req.get("gasLimit") or tx_req.get("gas")) - gas_price = _hi(tx_req.get("maxFeePerGas") or tx_req.get("gasPrice") or 0) - tx_value = _hi(tx_req.get("value")) - eth_needed = tx_value + gas_limit * gas_price * 2 - eth_balance = await loop.run_in_executor(None, w3.eth.get_balance, address) - - if eth_balance < eth_needed: - logger.warning( - "[Retry] [%s] Still not enough ETH for %s: have %d need %d", - debank_key, symbol, eth_balance, eth_needed, - ) - continue - - spender = tx_req.get("to", "") - if spender: - approved = await loop.run_in_executor( - None, ensure_erc20_approval, - w3, contract, address, spender, from_amount, private_key, - ) - if not approved: - continue - - tx_hash, receipt = await loop.run_in_executor( - None, sign_and_send, w3, tx_req, private_key, address - ) - if receipt is None: - continue - - logger.info( - "[Retry] [%s] Swap %s → native | tx: %s | block %s", - debank_key, symbol, tx_hash, receipt.blockNumber, - ) - tokens_swapped += 1 - chain_swapped += 1 - total_usd += value_usd - - delay = random.randint(settings.delay_min, settings.delay_max) - for _ in range(delay): - if stop_event.is_set(): - break - await asyncio.sleep(1) - - except InsufficientFundsError: - logger.warning("[Retry] [%s] Insufficient ETH for %s swap (node rejected)", debank_key, symbol) - except LiFiNoRouteError as e: - logger.info("[Retry] [%s] No route for %s: %s", debank_key, symbol, e) - except Exception as e: - logger.error("[Retry] [%s] Swap error for %s: %s", debank_key, symbol, e) - - if chain_swapped > 0: - chains_processed.append(debank_key) - logger.info("[Retry] [%s] Swapped %d tokens after refuel", debank_key, chain_swapped) - - return { - "chains_processed": ", ".join(chains_processed), - "tokens_swapped": tokens_swapped, - "total_usd": round(total_usd, 2), - } diff --git a/app/modules/token_collector/_signer.py b/app/modules/token_collector/_signer.py deleted file mode 100644 index d0cd341..0000000 --- a/app/modules/token_collector/_signer.py +++ /dev/null @@ -1,220 +0,0 @@ -# app/modules/token_collector/_signer.py -from __future__ import annotations -import logging -from typing import Any - -from eth_account import Account -from web3 import Web3 - -logger = logging.getLogger(__name__) - -# BIP44 деривационный путь для Ethereum -BIP44_PATH = "m/44'/60'/0'/0/0" - - -class TransactionReverted(RuntimeError): - """Транзакция отправлена и подтверждена, но reverted (status=0).""" - def __init__(self, tx_hash: str) -> None: - super().__init__(tx_hash) - self.tx_hash = tx_hash - - -class InsufficientFundsError(ValueError): - """Нода отклонила транзакцию: недостаточно ETH для gas * price + value.""" - -_ERC20_ABI = [ - { - "inputs": [{"name": "owner", "type": "address"}, {"name": "spender", "type": "address"}], - "name": "allowance", - "outputs": [{"name": "", "type": "uint256"}], - "type": "function", - "stateMutability": "view", - }, - { - "inputs": [{"name": "spender", "type": "address"}, {"name": "amount", "type": "uint256"}], - "name": "approve", - "outputs": [{"name": "", "type": "bool"}], - "type": "function", - "stateMutability": "nonpayable", - }, -] -_MAX_UINT256 = 2 ** 256 - 1 - - -def ensure_erc20_approval( - w3: Web3, - token_address: str, - owner: str, - spender: str, - amount: int, - private_key: str, -) -> bool: - """ - Проверяет allowance ERC-20 и при необходимости отправляет approve(spender, MAX_UINT256). - Возвращает True если allowance достаточен или approve прошёл успешно. - """ - token = w3.eth.contract( - address=Web3.to_checksum_address(token_address), - abi=_ERC20_ABI, - ) - allowance = token.functions.allowance( - Web3.to_checksum_address(owner), - Web3.to_checksum_address(spender), - ).call() - - if allowance >= amount: - return True - - logger.info("Approving %s → spender %s...", token_address[:10], spender[:10]) - - nonce = w3.eth.get_transaction_count(Web3.to_checksum_address(owner), "pending") - try: - base_fee = w3.eth.get_block("latest")["baseFeePerGas"] - tx = token.functions.approve( - Web3.to_checksum_address(spender), _MAX_UINT256 - ).build_transaction({ - "from": Web3.to_checksum_address(owner), - "nonce": nonce, - "maxFeePerGas": base_fee * 11 // 10, - "maxPriorityFeePerGas": 1, # минимум 1 wei — некоторые RPC отклоняют 0 - }) - except Exception: - tx = token.functions.approve( - Web3.to_checksum_address(spender), _MAX_UINT256 - ).build_transaction({ - "from": Web3.to_checksum_address(owner), - "nonce": nonce, - "gasPrice": w3.eth.gas_price * 2, - }) - - signed = w3.eth.account.sign_transaction(tx, private_key) - try: - tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction) - except Exception as e: - if "insufficient funds" in str(e).lower(): - raise InsufficientFundsError(str(e)) - raise - - try: - receipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=60) - except Exception as e: - logger.warning("Approve receipt timeout for %s: %s", tx_hash.hex(), e) - return True # tx отправлена — считаем что одобрено, swap попробует сам - - if receipt.status == 0: - logger.error("Approve reverted: %s", tx_hash.hex()) - return False - - logger.info("Approve confirmed: %s", tx_hash.hex()) - return True - - -def derive_address(wallet_raw: str, wallet_type: str) -> tuple[str, str]: - """ - Возвращает (address, private_key_hex). - wallet_type: "private_key" | "mnemonic" - Бросает ValueError для неизвестного типа. - """ - if wallet_type == "private_key": - acct = Account.from_key(wallet_raw) - return acct.address, wallet_raw - elif wallet_type == "mnemonic": - Account.enable_unaudited_hdwallet_features() - acct = Account.from_mnemonic(wallet_raw, account_path=BIP44_PATH) - return acct.address, acct.key.hex() - else: - raise ValueError(f"Неизвестный тип кошелька: {wallet_type}") - - -def sign_and_send( - w3: Web3, - tx_req: dict, - private_key: str, - address: str, -) -> tuple[str, Any] | tuple[str, None]: - """ - Подписывает и отправляет транзакцию. Возвращает (tx_hash_hex, receipt). - Если receipt.status == 0 (reverted) — логирует и возвращает (tx_hash_hex, None). - """ - nonce = w3.eth.get_transaction_count(address, "pending") - - def _parse(v) -> int: - """Парсит hex (0x-префикс) или decimal строку/int в int.""" - if isinstance(v, str): - return int(v, 16) if v.startswith("0x") else int(v) - return int(v) - - # EIP-1559 (если есть maxFeePerGas) - if "maxFeePerGas" in tx_req: - tx = { - "from": address, - "to": Web3.to_checksum_address(tx_req["to"]), - "data": tx_req.get("data", "0x"), - "value": _parse(tx_req["value"]), - "gas": _parse(tx_req.get("gasLimit") or tx_req.get("gas")), - "maxFeePerGas": _parse(tx_req["maxFeePerGas"]), - "maxPriorityFeePerGas": _parse(tx_req["maxPriorityFeePerGas"]), - "nonce": nonce, - "chainId": _parse(tx_req["chainId"]), - } - # Берём актуальный baseFee из сети и ставим maxFeePerGas = baseFee×2 + priority. - # На L2 (OP, ARB, Base) LI.FI выставляет priority=0 — это нормально, sequencer не требует чаевых. - # НЕ форсируем минимум 1 gwei: 0 or 1e9 завышает газ в 100–1000x на L2. - try: - base_fee = w3.eth.get_block("latest")["baseFeePerGas"] - # max(1, ...) — некоторые RPC (publicnode/OP и др.) отклоняют tip=0 - priority = max(1, tx["maxPriorityFeePerGas"]) - tx["maxPriorityFeePerGas"] = priority - tx["maxFeePerGas"] = base_fee * 11 // 10 + priority - logger.info("maxFeePerGas set to %d (baseFee=%d priority=%d)", tx["maxFeePerGas"], base_fee, priority) - except Exception: - tx["maxFeePerGas"] = int(tx["maxFeePerGas"] * 2) - else: - # Legacy - tx = { - "from": address, - "to": Web3.to_checksum_address(tx_req["to"]), - "data": tx_req.get("data", "0x"), - "value": _parse(tx_req["value"]), - "gas": _parse(tx_req.get("gasLimit") or tx_req.get("gas")), - "gasPrice": _parse(tx_req["gasPrice"]), - "nonce": nonce, - "chainId": _parse(tx_req["chainId"]), - } - # Бампаем gasPrice на случай роста baseFee - try: - base_fee = w3.eth.get_block("latest").get("baseFeePerGas") or 0 - if base_fee: - tx["gasPrice"] = max(tx["gasPrice"], base_fee * 2) - else: - tx["gasPrice"] = int(tx["gasPrice"] * 2) - except Exception: - tx["gasPrice"] = int(tx["gasPrice"] * 2) - - for _attempt in range(3): - signed = w3.eth.account.sign_transaction(tx, private_key) - try: - tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction) - break - except Exception as e: - e_str = str(e) - if "nonce too low" in e_str and _attempt < 2: - tx["nonce"] = w3.eth.get_transaction_count(address, "latest") - logger.warning("Nonce too low, retrying with nonce=%d (attempt %d)", tx["nonce"], _attempt + 2) - continue - if "insufficient funds" in e_str.lower(): - raise InsufficientFundsError(e_str) - raise - tx_hash_hex = "0x" + tx_hash.hex() - - try: - receipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=60) - except Exception as e: - logger.warning("Receipt fetch failed for %s (%s), will rely on bridge poller", tx_hash_hex, e) - return tx_hash_hex, None - - if receipt.status == 0: - logger.error("Транзакция reverted: %s", tx_hash_hex) - raise TransactionReverted(tx_hash_hex) - - return tx_hash_hex, receipt diff --git a/app/modules/twitter_checker/__init__.py b/app/modules/twitter_checker/__init__.py deleted file mode 100644 index 4d4565d..0000000 --- a/app/modules/twitter_checker/__init__.py +++ /dev/null @@ -1,147 +0,0 @@ -from __future__ import annotations -import asyncio -import threading -from typing import AsyncIterator - -from PySide6.QtCore import QObject, Signal -from PySide6.QtWidgets import QWidget - -from app.core.base_module import BaseModule -from app.core.models import RunContext, Result, ResultStatus, ColumnDef -from app.integrations.proxy_utils import ProxyRotator -from app.integrations.twitter_client import ( - TwitterClient, - TwitterTokenStatus, -) - -RETRY_ATTEMPTS = 3 - - -def _check_token_sync( - token: str, - rotator: ProxyRotator, - stop_event: threading.Event, -) -> Result: - """Module-level sync function for run_in_executor. Checks one auth_token.""" - last_error: str = "Unknown error" - - for _ in range(RETRY_ATTEMPTS): - if stop_event.is_set(): - return Result( - item=token, - status=ResultStatus.ERROR, - error="Stopped", - data={"username": None, "account_status": "error"}, - ) - - proxy = rotator.next() - if proxy is None: - return Result( - item=token, - status=ResultStatus.ERROR, - error="No proxies available", - data={"username": None, "account_status": "error"}, - ) - - try: - r = TwitterClient(proxy.to_url()).check_token(token) - - if r.status == TwitterTokenStatus.OK: - return Result( - item=token, - status=ResultStatus.OK, - data={"username": r.username, "account_status": "ok"}, - ) - - if r.status in ( - TwitterTokenStatus.INVALID, - TwitterTokenStatus.SUSPENDED, - TwitterTokenStatus.LOCKED, - ): - # Definitive answer — do not retry - return Result( - item=token, - status=ResultStatus.SKIP, - error=r.status.value, - data={"username": None, "account_status": r.status.value}, - ) - - last_error = "API error" - except Exception as e: - last_error = str(e) - - return Result( - item=token, - status=ResultStatus.ERROR, - error=last_error, - data={"username": None, "account_status": "error"}, - ) - - -class _TwitterSignals(QObject): - # Created in __init__ (main thread) for correct Qt thread affinity - run_complete = Signal(list) # list[Result] - - -class TwitterCheckerModule(BaseModule): - name = "Twitter Checker" - - def column_schema(self) -> list[ColumnDef]: - return [ - ColumnDef(key="item", label="Token", width=200), - ColumnDef(key="account_status", label="Status"), - ColumnDef(key="username", label="Username"), - ] - - def __init__(self) -> None: - from app.ui.module_views.twitter_checker_view import TwitterCheckerConfigWidget - self._signals = _TwitterSignals() - self._results: list[Result] = [] - self._widget = TwitterCheckerConfigWidget() - self._signals.run_complete.connect(self._widget.on_run_complete) - self._stop_event = threading.Event() - - def get_config_widget(self) -> QWidget: - return self._widget - - def get_item_count(self) -> int: - return len(self._widget.get_tokens()) - - async def run(self, ctx: RunContext) -> AsyncIterator[Result]: - self._results.clear() - self._stop_event.clear() - - tokens = self._widget.get_tokens() - proxies = self._widget.get_proxies() - rotator = ProxyRotator(proxies) - semaphore = asyncio.Semaphore(ctx.concurrency) - loop = asyncio.get_running_loop() - - async def _indexed_check(idx: int, token: str) -> tuple[int, Result]: - async with semaphore: - result = await loop.run_in_executor( - None, _check_token_sync, token, rotator, self._stop_event - ) - return idx, result - - tasks = [asyncio.create_task(_indexed_check(i, t)) for i, t in enumerate(tokens)] - buffer: dict[int, Result] = {} - next_idx = 0 - try: - for fut in asyncio.as_completed(tasks): - if self._stop_event.is_set(): - for t in tasks: - t.cancel() - break - idx, result = await fut - buffer[idx] = result - while next_idx in buffer: - r = buffer.pop(next_idx) - self._results.append(r) - yield r - next_idx += 1 - finally: - self._signals.run_complete.emit(list(self._results)) - - async def stop(self) -> None: - self._stop_event.set() diff --git a/app/resources/.gitkeep b/app/resources/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/app/resources/config.yaml b/app/resources/config.yaml deleted file mode 100644 index 91591bc..0000000 --- a/app/resources/config.yaml +++ /dev/null @@ -1,11 +0,0 @@ -concurrency: 10 -language: ru -log_level: INFO -retry_attempts: 3 -retry_backoff: 2.0 -rpc_evm_default: -- https://eth.llamarpc.com -- https://bsc-dataseed.binance.org -rpc_solana_default: -- https://api.mainnet-beta.solana.com -timeout: 30 diff --git a/app/resources/icon.png b/app/resources/icon.png deleted file mode 100644 index 887edcc..0000000 Binary files a/app/resources/icon.png and /dev/null differ diff --git a/app/resources/translations/__init__.py b/app/resources/translations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/app/resources/translations/en.py b/app/resources/translations/en.py deleted file mode 100644 index 00296a7..0000000 --- a/app/resources/translations/en.py +++ /dev/null @@ -1,127 +0,0 @@ -TRANSLATIONS: dict[str, str] = { - # Toolbar - "start_btn": "▶ Start", - "stop_btn": "■ Stop", - "faq_btn": "FAQ", - - # File inputs - "wallets_label": "Wallets:", - "proxy_label": "Proxy: ", - "tokens_label_input": "Tokens: ", - "browse_btn": "Browse", - "file_not_loaded": "File not loaded", - "n_addresses_loaded": "✓ {n} addresses loaded", - "n_wallets_loaded": "✓ {n} wallets loaded", - "n_proxies_loaded": "✓ {n} proxies loaded", - "n_tokens_loaded": "✓ {n} tokens loaded", - "error_fmt": "Error: {e}", - - # Files header (collector) - "files_header": "Files", - - # Export group - "export_group": "Export", - "tokens_checkbox": "Tokens", - "tokens_filter_label": "Tokens:", - "filter_all": "All", - "export_btn": "💾 Export", - - # Export dialogs - "save_csv": "Save CSV", - "save_json": "Save JSON", - "save_xlsx": "Save XLSX", - "save_error_title": "Save Error", - "save_error_msg": "Could not save file:\n{path}\n\nClose the file if it is open in Excel.", - - # Filter group (collector) - "filters_group": "Filters", - "min_value_label": "Min. token value ($):", - "exclude_chains_label": "Exclude chains:", - "slippage_label": "Slippage (%):", - - # Bridge group (collector) - "bridge_group": "Bridge", - "target_chains_label": "Target chains:", - - # Delays group (collector) - "delays_group": "Delays", - "delay_between_wallets_label": "Between wallets (sec):", - - # Exchange group (collector) - "exchange_group": "Exchange Subaccount", - "send_to_exchange_checkbox": "Send to exchange subaccount", - "subaccounts_label": "Subaccounts:", - "after_bridge_delay_label": "Delay after bridge (sec):", - "wallets_subaccounts_mismatch": "⚠ Wallets: {w}, subaccounts: {s}", - "n_subaccounts_loaded": "✓ {s} subaccounts loaded", - - # Twitter/Discord checker - "status_label": "Status:", - "format_label": "Format:", - "proxy_label_checker": "Proxy: ", - "tokens_label_checker": "Tokens: ", - - # Results table - "search_placeholder": "Search...", - "total_fmt": "Total: ${total:,.2f}", - "export_csv_btn": "Export CSV", - "export_json_btn": "Export JSON", - "export_xlsx_btn": "Export XLSX", - - # Log widget - "clear_log_btn": "Clear log", - - # File dialogs - "select_wallets_file": "Select wallets file", - "select_proxy_file": "Select proxy file", - "select_subaccounts_file": "Select subaccounts file", - "select_tokens_file": "Select tokens file", - - # RPC - "rpc_url_label": "RPC URL:", - - # Chain picker - "pick_chains_placeholder": "select chains...", - "chain_search_placeholder": "Search…", - - # Placeholder view - "placeholder_module_msg": "Module: {module_name}\n\nSettings will be added in the next iteration.", - - # SVM token filter - "all_tokens": "All", - "empty_results_title": "No results yet", - "empty_results_subtitle": "Load a file and press Start to begin", - "empty_log_title": "Log is empty", - "empty_log_subtitle": "Activity will appear here during execution", - - # Drop zone - "drop_hint": "Drop file here or click Browse", - "select_file": "Select file", - - # Collector tooltips - "min_value_tooltip": "Tokens below this USD value will be skipped", - "slippage_tooltip": "Maximum allowed price slippage for swaps", - "delay_min_tooltip": "Minimum delay between processing wallets", - "delay_max_tooltip": "Maximum delay between processing wallets", - "after_bridge_delay_tooltip": "Wait time after bridge before next operation", - "min_bridge_tooltip": "Skip bridge if balance is below this USD value", - - # Sidebar - "sidebar_collapse_tooltip": "Collapse sidebar", - "sidebar_expand_tooltip": "Expand sidebar", - - "freeze_log_tooltip": "Freeze log (pause auto-scroll)", - - "toast_finished_ok": "✓ Completed successfully", - "toast_finished_errors": "⚠ Completed with errors", - - # Validation - "validation_no_wallets": "Load wallets file first", - "validation_no_proxies": "Load proxies file first", - "validation_no_tokens": "Load tokens file first", - "validation_no_items": "Load input file first", - - # Log collapse - "log_expand_btn": "▲ Log", - "log_collapse_btn": "▼ Log", -} diff --git a/app/resources/translations/ru.py b/app/resources/translations/ru.py deleted file mode 100644 index 6f17bad..0000000 --- a/app/resources/translations/ru.py +++ /dev/null @@ -1,127 +0,0 @@ -TRANSLATIONS: dict[str, str] = { - # Toolbar - "start_btn": "▶ Старт", - "stop_btn": "■ Стоп", - "faq_btn": "FAQ", - - # File inputs - "wallets_label": "Кошельки:", - "proxy_label": "Прокси: ", - "tokens_label_input": "Токены: ", - "browse_btn": "Обзор", - "file_not_loaded": "Файл не загружен", - "n_addresses_loaded": "✓ {n} адресов загружено", - "n_wallets_loaded": "✓ {n} кошельков загружено", - "n_proxies_loaded": "✓ {n} прокси загружено", - "n_tokens_loaded": "✓ {n} токенов загружено", - "error_fmt": "Ошибка: {e}", - - # Files header (collector) - "files_header": "Файлы", - - # Export group - "export_group": "Экспорт", - "tokens_checkbox": "Токены", - "tokens_filter_label": "Токены:", - "filter_all": "Все", - "export_btn": "💾 Экспорт", - - # Export dialogs - "save_csv": "Сохранить CSV", - "save_json": "Сохранить JSON", - "save_xlsx": "Сохранить XLSX", - "save_error_title": "Ошибка сохранения", - "save_error_msg": "Не удалось сохранить файл:\n{path}\n\nЗакройте файл если он открыт в Excel.", - - # Filter group (collector) - "filters_group": "Фильтры", - "min_value_label": "Мин. стоимость токена ($):", - "exclude_chains_label": "Исключить сети:", - "slippage_label": "Slippage (%):", - - # Bridge group (collector) - "bridge_group": "Бридж", - "target_chains_label": "Целевые сети:", - - # Delays group (collector) - "delays_group": "Задержки", - "delay_between_wallets_label": "Между кошельками (сек):", - - # Exchange group (collector) - "exchange_group": "Субаккаунт биржи", - "send_to_exchange_checkbox": "Отправить на субаккаунт биржи", - "subaccounts_label": "Субаккаунты:", - "after_bridge_delay_label": "Задержка после бриджа (сек):", - "wallets_subaccounts_mismatch": "⚠ Кошельков: {w}, субаккаунтов: {s}", - "n_subaccounts_loaded": "✓ {s} субаккаунтов загружено", - - # Twitter/Discord checker - "status_label": "Статус:", - "format_label": "Формат:", - "proxy_label_checker": "Прокси: ", - "tokens_label_checker": "Токены: ", - - # Results table - "search_placeholder": "Поиск...", - "total_fmt": "Итого: ${total:,.2f}", - "export_csv_btn": "Экспорт CSV", - "export_json_btn": "Экспорт JSON", - "export_xlsx_btn": "Экспорт XLSX", - - # Log widget - "clear_log_btn": "Очистить лог", - - # File dialogs - "select_wallets_file": "Выбрать файл кошельков", - "select_proxy_file": "Выбрать файл прокси", - "select_subaccounts_file": "Выбрать файл субаккаунтов", - "select_tokens_file": "Выбрать файл токенов", - - # RPC - "rpc_url_label": "RPC URL:", - - # Chain picker - "pick_chains_placeholder": "выберите сети...", - "chain_search_placeholder": "Поиск…", - - # Placeholder view - "placeholder_module_msg": "Модуль: {module_name}\n\nНастройки будут добавлены в следующей итерации.", - - # SVM token filter - "all_tokens": "Все", - "empty_results_title": "Результатов пока нет", - "empty_results_subtitle": "Загрузите файл и нажмите Старт", - "empty_log_title": "Лог пуст", - "empty_log_subtitle": "Активность появится здесь во время выполнения", - - # Drop zone - "drop_hint": "Перетащите файл сюда или нажмите Обзор", - "select_file": "Выбрать файл", - - # Collector tooltips - "min_value_tooltip": "Токены дешевле этой суммы в USD будут пропущены", - "slippage_tooltip": "Максимально допустимое проскальзывание цены при свапе", - "delay_min_tooltip": "Минимальная задержка между обработкой кошельков", - "delay_max_tooltip": "Максимальная задержка между обработкой кошельков", - "after_bridge_delay_tooltip": "Время ожидания после бриджа перед следующей операцией", - "min_bridge_tooltip": "Пропустить бридж если баланс ниже этой суммы в USD", - - # Sidebar - "sidebar_collapse_tooltip": "Свернуть панель", - "sidebar_expand_tooltip": "Развернуть панель", - - "freeze_log_tooltip": "Заморозить лог (пауза авто-прокрутки)", - - "toast_finished_ok": "✓ Выполнено успешно", - "toast_finished_errors": "⚠ Выполнено с ошибками", - - # Validation - "validation_no_wallets": "Сначала загрузите файл кошельков", - "validation_no_proxies": "Сначала загрузите файл прокси", - "validation_no_tokens": "Сначала загрузите файл токенов", - "validation_no_items": "Сначала загрузите входной файл", - - # Log collapse - "log_expand_btn": "▲ Лог", - "log_collapse_btn": "▼ Лог", -} diff --git a/app/storage/__init__.py b/app/storage/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/app/storage/discord_exporter.py b/app/storage/discord_exporter.py deleted file mode 100644 index 6fcbdc0..0000000 --- a/app/storage/discord_exporter.py +++ /dev/null @@ -1,71 +0,0 @@ -from __future__ import annotations -import csv -import json -from dataclasses import dataclass - -import openpyxl - -from app.core.models import Result - - -@dataclass -class DiscordExportConfig: - status_filter: str | None = None # None = all; "ok"/"invalid"/"disabled" - - -class DiscordExporter: - _COLUMNS = ["token", "username", "email", "has_phone", "account_status"] - - def build( - self, - results: list[Result], - config: DiscordExportConfig, - ) -> list[dict]: - rows = [] - for r in results: - account_status = r.data.get("account_status", "") - if config.status_filter and account_status != config.status_filter: - continue - rows.append({ - "token": r.item, - "username": r.data.get("username") or "", - "email": r.data.get("email") or "", - "has_phone": "yes" if r.data.get("has_phone") else "no", - "account_status": account_status, - }) - return rows - - def export( - self, - results: list[Result], - config: DiscordExportConfig, - path: str, - fmt: str, - ) -> None: - rows = self.build(results, config) - if fmt == "csv": - self._export_csv(rows, path) - elif fmt == "json": - self._export_json(rows, path) - else: - self._export_xlsx(rows, path) - - def _export_csv(self, rows: list[dict], path: str) -> None: - with open(path, "w", newline="", encoding="utf-8") as f: - writer = csv.DictWriter(f, fieldnames=self._COLUMNS) - writer.writeheader() - writer.writerows(rows) - - def _export_json(self, rows: list[dict], path: str) -> None: - json_rows = [{**row, "has_phone": row["has_phone"] == "yes"} for row in rows] - with open(path, "w", encoding="utf-8") as f: - json.dump(json_rows, f, ensure_ascii=False, indent=2) - - def _export_xlsx(self, rows: list[dict], path: str) -> None: - wb = openpyxl.Workbook() - ws = wb.active - ws.title = "Results" - ws.append(self._COLUMNS) - for row in rows: - ws.append([row.get(c, "") for c in self._COLUMNS]) - wb.save(path) diff --git a/app/storage/evm_exporter.py b/app/storage/evm_exporter.py deleted file mode 100644 index d4819c5..0000000 --- a/app/storage/evm_exporter.py +++ /dev/null @@ -1,157 +0,0 @@ -from __future__ import annotations -import csv -import json -from dataclasses import dataclass -from pathlib import Path - -import openpyxl - -from app.core.models import Result - - -@dataclass -class EvmExportConfig: - summary: bool = True - tokens: bool = True - token_filter: str | None = None - - -def _parse_filter(f: str | None) -> tuple[str, str] | None: - """ - Разбирает фильтр "name:chain" по первому ':'. - Возвращает (name_part, chain_part) или None если фильтр отключён. - None/"" → None. Строка без ':' → None (фильтр отключён). - """ - if not f: - return None - if ":" not in f: - return None # без ':' фильтр не работает - idx = f.index(":") - return f[:idx].strip(), f[idx + 1:].strip() - - -def _match_token(t: dict, flt: tuple[str, str] | None) -> bool: - if flt is None: - return True - name_part, chain_part = flt - if name_part and (t.get("symbol") or "").upper() != name_part.upper(): - return False - if chain_part and (t.get("chain") or "").lower() != chain_part.lower(): - return False - return True - - -class EvmExporter: - """Строит и экспортирует данные EVM Balance по конфигурации.""" - - def build( - self, - results: list[Result], - details: dict[str, dict], - config: EvmExportConfig, - ) -> dict[str, list[dict]]: - tok_flt = _parse_filter(config.token_filter) - data: dict[str, list[dict]] = {"summary": [], "tokens": []} - - for r in results: - if config.summary: - data["summary"].append({ - "address": r.item, - "total_usd": r.data.get("total_usd", ""), - "tokens_usd": r.data.get("tokens_usd", ""), - "tokens": r.data.get("tokens", ""), - "chains": r.data.get("chains", ""), - "status": r.status.value, - }) - - if r.status.value != "ok": - continue - - detail = details.get(r.item, {}) - if config.tokens: - for t in detail.get("tokens_data", []): - if _match_token(t, tok_flt): - data["tokens"].append({ - "address": r.item, - "symbol": t.get("symbol", ""), - "chain": t.get("chain", ""), - "amount": t.get("amount", 0), - "price": t.get("price", 0), - "value": t.get("value", 0), - }) - - return data - - def export( - self, - results: list[Result], - details: dict[str, dict], - config: EvmExportConfig, - path: str, - fmt: str, - ) -> None: - data = self.build(results, details, config) - if fmt == "csv": - self._export_csv(data, config, path) - elif fmt == "json": - self._export_json(data, path) - else: - self._export_xlsx(data, config, path) - - # --- CSV --- - - _SECTION_COLUMNS = { - "summary": ["address", "total_usd", "tokens_usd", "tokens", "chains", "status"], - "tokens": ["address", "symbol", "chain", "amount", "price", "value"], - } - _SECTION_LABELS = { - "summary": "Summary", - "tokens": "Tokens", - } - _SECTION_FLAGS = { - "summary": "summary", - "tokens": "tokens", - } - - def _active_sections(self, config: EvmExportConfig) -> list[str]: - return [k for k in ("summary", "tokens") if getattr(config, self._SECTION_FLAGS[k])] - - def _export_csv(self, data: dict, config: EvmExportConfig, path: str) -> None: - active = self._active_sections(config) - with open(path, "w", newline="", encoding="utf-8") as f: - writer = csv.writer(f) - for i, section in enumerate(active): - if i > 0: - writer.writerow([]) # пустая строка-разделитель - writer.writerow([f"=== {self._SECTION_LABELS[section]} ==="]) - cols = self._SECTION_COLUMNS[section] - writer.writerow(cols) - for row in data[section]: - writer.writerow([row.get(c, "") for c in cols]) - - # --- JSON --- - - def _export_json(self, data: dict, path: str) -> None: - with open(path, "w", encoding="utf-8") as f: - json.dump(data, f, ensure_ascii=False, indent=2) - - # --- XLSX --- - - def _export_xlsx(self, data: dict, config: EvmExportConfig, path: str) -> None: - active = self._active_sections(config) - wb = openpyxl.Workbook() - wb.remove(wb.active) # удаляем дефолтный лист - - if not active: - ws = wb.create_sheet("Summary") - ws.append(self._SECTION_COLUMNS["summary"]) - else: - sheet_names = {"summary": "Summary", "tokens": "Tokens"} - for section in active: - ws = wb.create_sheet(sheet_names[section]) - cols = self._SECTION_COLUMNS[section] - ws.append(cols) - for row in data[section]: - ws.append([row.get(c, "") for c in cols]) - - wb.save(path) diff --git a/app/storage/exporters.py b/app/storage/exporters.py deleted file mode 100644 index 9904548..0000000 --- a/app/storage/exporters.py +++ /dev/null @@ -1,61 +0,0 @@ -from __future__ import annotations -import csv -import json -from abc import ABC, abstractmethod -from pathlib import Path -import openpyxl -from app.core.models import Result - - -def get_columns(results: list[Result]) -> list[str]: - """Определяет колонки из первого непустого result.data. Fallback: item, status.""" - for r in results: - if r.data: - base = ["item", "status"] - extra = [k for k in r.data.keys() if k not in base] - return base + extra - return ["item", "status"] - - -def _result_to_row(result: Result, columns: list[str]) -> dict: - row = { - "item": result.item, - "status": result.status.value, - } - row.update(result.data) - return {col: row.get(col, "") for col in columns} - - -class BaseExporter(ABC): - @abstractmethod - def export(self, results: list[Result], path: str) -> None: ... - - -class CsvExporter(BaseExporter): - def export(self, results: list[Result], path: str) -> None: - columns = get_columns(results) - with open(path, "w", newline="", encoding="utf-8") as f: - writer = csv.DictWriter(f, fieldnames=columns) - writer.writeheader() - for r in results: - writer.writerow(_result_to_row(r, columns)) - - -class JsonExporter(BaseExporter): - def export(self, results: list[Result], path: str) -> None: - columns = get_columns(results) - rows = [_result_to_row(r, columns) for r in results] - with open(path, "w", encoding="utf-8") as f: - json.dump(rows, f, ensure_ascii=False, indent=2) - - -class XlsxExporter(BaseExporter): - def export(self, results: list[Result], path: str) -> None: - columns = get_columns(results) - wb = openpyxl.Workbook() - ws = wb.active - ws.append(columns) - for r in results: - row = _result_to_row(r, columns) - ws.append([row.get(col, "") for col in columns]) - wb.save(path) diff --git a/app/storage/parsers.py b/app/storage/parsers.py deleted file mode 100644 index db0fae7..0000000 --- a/app/storage/parsers.py +++ /dev/null @@ -1,83 +0,0 @@ -from __future__ import annotations -from pathlib import Path -from urllib.parse import urlparse -from app.core.models import ProxyConfig -from app.core.exceptions import ParseError - - -def parse_lines(filepath: str) -> list[str]: - """Читает файл, удаляет пустые строки и комментарии (#).""" - path = Path(filepath) - if not path.exists(): - raise FileNotFoundError(f"File not found: {filepath}") - lines = [] - for line in path.read_text(encoding="utf-8").splitlines(): - line = line.strip() - if line and not line.startswith("#"): - lines.append(line) - return lines - - -def parse_proxies(filepath: str) -> list[ProxyConfig]: - """Парсит файл с прокси в произвольном формате.""" - return [_parse_proxy_line(line) for line in parse_lines(filepath) - if _parse_proxy_line(line) is not None] - - -def _parse_proxy_line(line: str) -> ProxyConfig | None: - """ - Алгоритм (приоритет сверху вниз): - 1. Содержит :// → формат с протоколом - 2. Содержит @ (без ://) → user:pass@host:port - 3. Разбить по : → 2 части = host:port, 4 части = host:port:user:pass - """ - try: - if "://" in line: - parsed = urlparse(line) - protocol = parsed.scheme or "http" - host = parsed.hostname or "" - port = parsed.port or 8080 - user = parsed.username - password = parsed.password - return ProxyConfig(host=host, port=int(port), user=user, password=password, protocol=protocol) - - if "@" in line: - credentials, hostport = line.rsplit("@", 1) - user, password = credentials.split(":", 1) - host, port_str = hostport.rsplit(":", 1) - return ProxyConfig(host=host, port=int(port_str), user=user, password=password) - - parts = line.split(":") - if len(parts) == 2: - return ProxyConfig(host=parts[0], port=int(parts[1])) - if len(parts) == 4: - return ProxyConfig(host=parts[0], port=int(parts[1]), user=parts[2], password=parts[3]) - - except (ValueError, AttributeError): - pass - return None - - -def parse_wallets(filepath: str) -> list[dict]: - """ - Определяет тип каждого кошелька: - - private_key: 0x + 64 hex символа - - mnemonic: 12 или 24 слова - - address: 0x + 40 hex символа - """ - result = [] - for line in parse_lines(filepath): - result.append({"raw": line, "type": _detect_wallet_type(line)}) - return result - - -def _detect_wallet_type(value: str) -> str: - import re - if re.fullmatch(r"0x[0-9a-fA-F]{64}", value): - return "private_key" - if re.fullmatch(r"0x[0-9a-fA-F]{40}", value): - return "address" - word_count = len(value.split()) - if word_count in (12, 24): - return "mnemonic" - return "unknown" diff --git a/app/storage/svm_exporter.py b/app/storage/svm_exporter.py deleted file mode 100644 index fce1934..0000000 --- a/app/storage/svm_exporter.py +++ /dev/null @@ -1,117 +0,0 @@ -from __future__ import annotations -import csv -import json -from dataclasses import dataclass -from pathlib import Path - -import openpyxl - -from app.core.models import Result, ResultStatus - - -@dataclass -class SvmExportConfig: - summary: bool = True - tokens: bool = True - token_filter: str | None = None # None = all; plain symbol e.g. "USDC" - - -class SvmExporter: - _SECTION_COLUMNS = { - "summary": ["address", "sol_balance", "sol_usd", "total_usd", "tokens", "status"], - "tokens": ["address", "symbol", "mint", "amount", "price", "value"], - } - _SECTION_LABELS = {"summary": "Summary", "tokens": "Tokens"} - - def build( - self, - results: list[Result], - details: dict[str, dict], - config: SvmExportConfig, - ) -> dict[str, list[dict]]: - data: dict[str, list[dict]] = {"summary": [], "tokens": []} - - for r in results: - if config.summary: - data["summary"].append({ - "address": r.item, - "sol_balance": r.data.get("sol_balance", ""), - "sol_usd": r.data.get("sol_usd", ""), - "total_usd": r.data.get("total_usd", ""), - "tokens": r.data.get("tokens", ""), - "status": r.status.value, - }) - - if r.status != ResultStatus.OK or not config.tokens: - continue - - detail = details.get(r.item, {}) - for token in detail.get("tokens_data", []): - if config.token_filter is not None: - if token.get("symbol", "").upper() != config.token_filter.upper(): - continue - data["tokens"].append({ - "address": r.item, - "symbol": token.get("symbol", ""), - "mint": token.get("mint", ""), - "amount": token.get("amount", 0), - "price": token.get("price", 0), - "value": token.get("value", 0), - }) - - return data - - def export( - self, - results: list[Result], - details: dict[str, dict], - config: SvmExportConfig, - path: str, - fmt: str, - ) -> None: - data = self.build(results, details, config) - if fmt == "csv": - self._export_csv(data, config, path) - elif fmt == "json": - self._export_json(data, path) - else: - self._export_xlsx(data, config, path) - - def _active_sections(self, config: SvmExportConfig) -> list[str]: - return [k for k in ("summary", "tokens") if getattr(config, k)] - - def _export_csv(self, data: dict, config: SvmExportConfig, path: str) -> None: - active = self._active_sections(config) - with open(path, "w", newline="", encoding="utf-8") as f: - writer = csv.writer(f) - for i, section in enumerate(active): - if i > 0: - writer.writerow([]) - writer.writerow([f"=== {self._SECTION_LABELS[section]} ==="]) - cols = self._SECTION_COLUMNS[section] - writer.writerow(cols) - for row in data[section]: - writer.writerow([row.get(c, "") for c in cols]) - - def _export_json(self, data: dict, path: str) -> None: - with open(path, "w", encoding="utf-8") as f: - json.dump(data, f, ensure_ascii=False, indent=2) - - def _export_xlsx(self, data: dict, config: SvmExportConfig, path: str) -> None: - active = self._active_sections(config) - wb = openpyxl.Workbook() - wb.remove(wb.active) - - if not active: - ws = wb.create_sheet("Summary") - ws.append(self._SECTION_COLUMNS["summary"]) - else: - sheet_names = {"summary": "Summary", "tokens": "Tokens"} - for section in active: - ws = wb.create_sheet(sheet_names[section]) - cols = self._SECTION_COLUMNS[section] - ws.append(cols) - for row in data[section]: - ws.append([row.get(c, "") for c in cols]) - - wb.save(path) diff --git a/app/storage/twitter_exporter.py b/app/storage/twitter_exporter.py deleted file mode 100644 index c06891e..0000000 --- a/app/storage/twitter_exporter.py +++ /dev/null @@ -1,69 +0,0 @@ -from __future__ import annotations -import csv -import json -from dataclasses import dataclass -from pathlib import Path - -import openpyxl - -from app.core.models import Result - - -@dataclass -class TwitterExportConfig: - status_filter: str | None = None # None = all; "ok"/"invalid"/"suspended"/"locked" - - -class TwitterExporter: - _COLUMNS = ["token", "username", "account_status"] - - def build( - self, - results: list[Result], - config: TwitterExportConfig, - ) -> list[dict]: - rows = [] - for r in results: - account_status = r.data.get("account_status", "") - if config.status_filter and account_status != config.status_filter: - continue - rows.append({ - "token": r.item, - "username": r.data.get("username") or "", - "account_status": account_status, - }) - return rows - - def export( - self, - results: list[Result], - config: TwitterExportConfig, - path: str, - fmt: str, - ) -> None: - rows = self.build(results, config) - if fmt == "csv": - self._export_csv(rows, path) - elif fmt == "json": - self._export_json(rows, path) - else: - self._export_xlsx(rows, path) - - def _export_csv(self, rows: list[dict], path: str) -> None: - with open(path, "w", newline="", encoding="utf-8") as f: - writer = csv.DictWriter(f, fieldnames=self._COLUMNS) - writer.writeheader() - writer.writerows(rows) - - def _export_json(self, rows: list[dict], path: str) -> None: - with open(path, "w", encoding="utf-8") as f: - json.dump(rows, f, ensure_ascii=False, indent=2) - - def _export_xlsx(self, rows: list[dict], path: str) -> None: - wb = openpyxl.Workbook() - ws = wb.active - ws.title = "Results" - ws.append(self._COLUMNS) - for row in rows: - ws.append([row.get(c, "") for c in self._COLUMNS]) - wb.save(path) diff --git a/app/ui/__init__.py b/app/ui/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/app/ui/log_widget.py b/app/ui/log_widget.py deleted file mode 100644 index fd2cbab..0000000 --- a/app/ui/log_widget.py +++ /dev/null @@ -1,178 +0,0 @@ -from __future__ import annotations - -from PySide6.QtWidgets import ( - QWidget, QVBoxLayout, QHBoxLayout, QPlainTextEdit, - QPushButton, QLineEdit, QStackedWidget, -) -from PySide6.QtGui import QColor, QTextCharFormat, QTextCursor -from PySide6.QtCore import Qt - -from app.ui.widgets.empty_state import EmptyState -from app.i18n import tr, i18n - - -class LogWidget(QWidget): - """Виджет лога с цветовой индикацией, фильтрацией, поиском и freeze.""" - - INFO_COLOR = "#8E8E93" - WARN_COLOR = "#FFD60A" - ERROR_COLOR = "#FF453A" - SUCCESS_COLOR = "#30D158" - - def __init__(self, parent=None) -> None: - super().__init__(parent) - self._all_lines: list[tuple[str, str]] = [] # (line, level) - self._frozen = False - self._active_filter: str = "all" - - layout = QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(4) - - # Controls row - controls = QHBoxLayout() - controls.setSpacing(4) - - self._btn_all = QPushButton("All") - self._btn_info = QPushButton("Info") - self._btn_warn = QPushButton("Warn") - self._btn_error = QPushButton("Error") - - for btn, filt in [ - (self._btn_all, "all"), - (self._btn_info, "info"), - (self._btn_warn, "warn"), - (self._btn_error, "error"), - ]: - btn.setCheckable(True) - btn.setProperty("logFilter", True) - btn.setFixedHeight(26) - btn.clicked.connect(lambda checked, f=filt: self._set_filter(f)) - controls.addWidget(btn) - self._btn_all.setChecked(True) - - controls.addStretch() - - self._search = QLineEdit() - self._search.setObjectName("logSearch") - self._search.setFixedWidth(180) - self._search.setClearButtonEnabled(True) - self._search.textChanged.connect(self._refilter) - controls.addWidget(self._search) - - self._freeze_btn = QPushButton("❄") - self._freeze_btn.setCheckable(True) - self._freeze_btn.setProperty("logFilter", True) - self._freeze_btn.setFixedSize(26, 26) - self._freeze_btn.setToolTip(tr("freeze_log_tooltip")) - self._freeze_btn.toggled.connect(self._on_freeze) - controls.addWidget(self._freeze_btn) - - self._clear_btn = QPushButton() - self._clear_btn.setProperty("logFilter", True) - self._clear_btn.setFixedHeight(26) - self._clear_btn.clicked.connect(self._clear) - controls.addWidget(self._clear_btn) - - layout.addLayout(controls) - - # Text display - self._text = QPlainTextEdit() - self._text.setReadOnly(True) - self._text.setMaximumBlockCount(5000) - self._text.setObjectName("logText") - - # Empty state - self._empty = EmptyState( - icon_text="📝", - title=tr("empty_log_title"), - subtitle=tr("empty_log_subtitle"), - ) - - self._stack = QStackedWidget() - self._stack.addWidget(self._empty) - self._stack.addWidget(self._text) - self._stack.setCurrentIndex(0) - - layout.addWidget(self._stack) - - self.retranslate_ui() - i18n.language_changed.connect(self.retranslate_ui) - - def retranslate_ui(self) -> None: - self._clear_btn.setText(tr("clear_log_btn")) - self._search.setPlaceholderText(tr("search_placeholder")) - self._freeze_btn.setToolTip(tr("freeze_log_tooltip")) - self._empty.set_texts( - title=tr("empty_log_title"), - subtitle=tr("empty_log_subtitle"), - ) - - def append(self, line: str) -> None: - level = "info" - if "ERROR" in line: - level = "error" - elif "WARN" in line: - level = "warn" - elif "SUCCESS" in line or "✓" in line: - level = "success" - - self._all_lines.append((line, level)) - self._stack.setCurrentIndex(1) - - if self._frozen: - return - - search = self._search.text().strip().lower() - if self._active_filter != "all" and level != self._active_filter: - return - if search and search not in line.lower(): - return - - self._append_colored(line, level) - - def _append_colored(self, line: str, level: str) -> None: - colors = { - "info": self.INFO_COLOR, - "warn": self.WARN_COLOR, - "error": self.ERROR_COLOR, - "success": self.SUCCESS_COLOR, - } - color = colors.get(level, self.INFO_COLOR) - cursor = self._text.textCursor() - cursor.movePosition(QTextCursor.End) - fmt = QTextCharFormat() - fmt.setForeground(QColor(color)) - cursor.setCharFormat(fmt) - cursor.insertText(line + "\n") - self._text.setTextCursor(cursor) - self._text.ensureCursorVisible() - - def _set_filter(self, filt: str) -> None: - self._active_filter = filt - for btn, f in [ - (self._btn_all, "all"), - (self._btn_info, "info"), - (self._btn_warn, "warn"), - (self._btn_error, "error"), - ]: - btn.setChecked(f == filt) - self._refilter() - - def _refilter(self) -> None: - self._text.clear() - search = self._search.text().strip().lower() - for line, level in self._all_lines: - if self._active_filter != "all" and level != self._active_filter: - continue - if search and search not in line.lower(): - continue - self._append_colored(line, level) - - def _on_freeze(self, checked: bool) -> None: - self._frozen = checked - - def _clear(self) -> None: - self._all_lines.clear() - self._text.clear() - self._stack.setCurrentIndex(0) diff --git a/app/ui/main_window.py b/app/ui/main_window.py deleted file mode 100644 index f17d44b..0000000 --- a/app/ui/main_window.py +++ /dev/null @@ -1,434 +0,0 @@ -from __future__ import annotations - -from PySide6.QtWidgets import ( - QMainWindow, QWidget, QHBoxLayout, QVBoxLayout, QSplitter, - QLabel, QPushButton, QStackedWidget, QProgressBar, -) -from PySide6.QtCore import Qt, QTimer, QUrl, QSettings -from PySide6.QtGui import QDesktopServices, QShortcut, QKeySequence - -from app.core.base_module import BaseModule -from app.core.module_registry import ModuleRegistry -from app.core.models import RunContext, Result -from app.core.logger import Logger -from app.core.task_runner import TaskRunner -from app.ui.log_widget import LogWidget -from app.ui.results_table import ResultsTable -from app.ui.sidebar import Sidebar -from app.ui.widgets.toast import show_toast -from app.i18n import i18n, tr - - -class MainWindow(QMainWindow): - def __init__(self, registry: ModuleRegistry) -> None: - super().__init__() - self.setWindowTitle("FarmerPro") - self.setMinimumSize(1200, 700) - self.setWindowState(self.windowState() | Qt.WindowMaximized) - - self._registry = registry - self._task_runner = TaskRunner(self) - self._task_runner.on_result.connect(self._on_result) - self._task_runner.on_log.connect(self._on_log) - self._task_runner.on_finished.connect(self._on_finished) - - self._current_module: BaseModule | None = None - self._total_count: int = 0 - self._done_count: int = 0 - self._config_widgets: dict[int, QWidget] = {} - self._results_cache: dict[int, list] = {} - - self._settings = QSettings("FarmerPro", "FarmerPro") - - # ── Sidebar ── - self._sidebar = Sidebar(registry.get_modules()) - self._sidebar.module_selected.connect(self._on_module_selected) - - # ── Top toolbar (simplified — FAQ + language only) ── - # Language toggle - self._lang_en_btn = QPushButton("EN") - self._lang_ru_btn = QPushButton("RU") - self._lang_en_btn.setCheckable(True) - self._lang_ru_btn.setCheckable(True) - self._lang_en_btn.setProperty("langToggle", True) - self._lang_ru_btn.setProperty("langToggle", True) - self._lang_en_btn.setFixedWidth(36) - self._lang_ru_btn.setFixedWidth(36) - self._lang_en_btn.clicked.connect(lambda: self._set_lang("en")) - self._lang_ru_btn.clicked.connect(lambda: self._set_lang("ru")) - self._update_lang_buttons() - - # FAQ button - self._faq_btn = QPushButton() - self._faq_btn.setProperty("langToggle", True) - self._faq_btn.setFixedWidth(46) - self._faq_btn.clicked.connect(self._open_faq) - - top_bar = QWidget() - top_bar.setObjectName("topToolbar") - top_layout = QHBoxLayout(top_bar) - top_layout.setContentsMargins(12, 8, 12, 8) - top_layout.setSpacing(10) - top_layout.addStretch() - top_layout.addWidget(self._faq_btn) - top_layout.addWidget(self._lang_en_btn) - top_layout.addWidget(self._lang_ru_btn) - - # ── Progress (improved: 6px height, percentage) ── - self._progress_bar = QProgressBar() - self._progress_bar.setTextVisible(False) - self._progress_bar.setFixedHeight(6) - self._progress_bar.setObjectName("mainProgress") - self._progress_label = QLabel("") - self._progress_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) - self._progress_label.setMinimumWidth(90) - self._progress_label.setObjectName("progressLabel") - - progress_row = QHBoxLayout() - progress_row.setContentsMargins(8, 2, 8, 2) - progress_row.addWidget(self._progress_bar) - progress_row.addWidget(self._progress_label) - - self._progress_widget = QWidget() - self._progress_widget.setLayout(progress_row) - self._progress_widget.setVisible(False) - - # ── Module config stack (LEFT pane now) ── - self._module_stack = QStackedWidget() - self._module_stack.setObjectName("moduleStack") - - # ── Start / Stop buttons — sticky footer inside config pane ── - self._start_btn = QPushButton() - self._start_btn.setProperty("primary", True) - self._stop_btn = QPushButton() - self._stop_btn.setEnabled(False) - - btn_row = QHBoxLayout() - btn_row.setContentsMargins(12, 8, 12, 8) - btn_row.setSpacing(8) - btn_row.addWidget(self._start_btn, 1) - btn_row.addWidget(self._stop_btn, 1) - - inspector_pane = QWidget() - inspector_pane.setObjectName("inspectorPane") - inspector_pane.setMinimumWidth(340) - inspector_layout = QVBoxLayout(inspector_pane) - inspector_layout.setContentsMargins(12, 12, 12, 0) - inspector_layout.addWidget(self._module_stack, 1) - inspector_layout.addLayout(btn_row) - - # ── Results & Log (RIGHT pane now) ── - self._results_table = ResultsTable() - self._log_widget = LogWidget() - self._log_widget.setObjectName("logPane") - - results_pane = QWidget() - results_pane.setObjectName("resultsPane") - results_layout = QVBoxLayout(results_pane) - results_layout.setContentsMargins(12, 12, 12, 12) - results_layout.addWidget(self._results_table) - - # ── Layout: Config LEFT | Results RIGHT ── - main_splitter = QSplitter(Qt.Horizontal) - main_splitter.setObjectName("mainSplitter") - main_splitter.addWidget(inspector_pane) - main_splitter.addWidget(results_pane) - main_splitter.setSizes([380, 720]) - - # ── Log: collapsed by default ── - self._log_widget.setMinimumHeight(36) - self._log_collapsed = True - - self._log_toggle_btn = QPushButton() - self._log_toggle_btn.setProperty("logFilter", True) - self._log_toggle_btn.setFixedHeight(28) - self._log_toggle_btn.clicked.connect(self._toggle_log) - - self._log_last_line = QLabel("") - self._log_last_line.setObjectName("statsLabel") - self._log_last_line.setStyleSheet("font-family: monospace; font-size: 9pt;") - - log_header = QHBoxLayout() - log_header.setContentsMargins(8, 4, 8, 4) - log_header.addWidget(self._log_toggle_btn) - log_header.addWidget(self._log_last_line, 1) - - log_container = QWidget() - log_container_layout = QVBoxLayout(log_container) - log_container_layout.setContentsMargins(0, 0, 0, 0) - log_container_layout.setSpacing(0) - log_container_layout.addLayout(log_header) - log_container_layout.addWidget(self._log_widget) - self._log_widget.setVisible(False) - - self._vertical_splitter = QSplitter(Qt.Vertical) - self._vertical_splitter.addWidget(main_splitter) - self._vertical_splitter.addWidget(log_container) - self._vertical_splitter.setSizes([660, 36]) - - # ── Right content area ── - content = QWidget() - content_layout = QVBoxLayout(content) - content_layout.setContentsMargins(0, 0, 0, 0) - content_layout.setSpacing(0) - content_layout.addWidget(top_bar) - content_layout.addWidget(self._progress_widget) - content_layout.addWidget(self._vertical_splitter) - - # ── Root: Sidebar + Content ── - root = QWidget() - root_layout = QHBoxLayout(root) - root_layout.setContentsMargins(0, 0, 0, 0) - root_layout.setSpacing(0) - root_layout.addWidget(self._sidebar) - root_layout.addWidget(content, 1) - self.setCentralWidget(root) - - # ── Signals ── - self._start_btn.clicked.connect(self._on_start) - self._stop_btn.clicked.connect(self._on_stop) - i18n.language_changed.connect(self.retranslate_ui) - - # ── Keyboard shortcuts ── - for i in range(min(9, len(registry.get_modules()))): - QShortcut(QKeySequence(f"Ctrl+{i+1}"), self).activated.connect( - lambda idx=i: self._sidebar.select_module(idx) - ) - QShortcut(QKeySequence("Ctrl+Return"), self).activated.connect(self._on_start) - QShortcut(QKeySequence("Escape"), self).activated.connect(self._on_stop) - QShortcut(QKeySequence("Ctrl+E"), self).activated.connect(self._quick_export) - - # Initial translation - self.retranslate_ui() - - # Restore splitter sizes - self._restore_splitter_sizes() - - # Show first module - if registry.get_modules(): - self._on_module_selected(registry.get_modules()[0]) - - # ── Language ── - - def _set_lang(self, lang: str) -> None: - from app.core.config import Config - i18n.set_language(lang) - Config.instance().set("language", lang) - self._update_lang_buttons() - - def _update_lang_buttons(self) -> None: - self._lang_en_btn.setChecked(i18n.language == "en") - self._lang_ru_btn.setChecked(i18n.language == "ru") - - def retranslate_ui(self) -> None: - self._start_btn.setText(tr("start_btn")) - self._stop_btn.setText(tr("stop_btn")) - self._faq_btn.setText(tr("faq_btn")) - self._log_toggle_btn.setText( - tr("log_expand_btn") if self._log_collapsed else tr("log_collapse_btn") - ) - for widget in self._config_widgets.values(): - if hasattr(widget, "retranslate_ui"): - widget.retranslate_ui() - self._results_table.retranslate_ui() - self._log_widget.retranslate_ui() - - def _open_faq(self) -> None: - urls = { - "en": "https://privatekey7.gitbook.io/farmerpro-en/", - "ru": "https://privatekey7.gitbook.io/farmerpro-ru/", - } - QDesktopServices.openUrl(QUrl(urls.get(i18n.language, urls["en"]))) - - # ── Log collapse / expand ── - - def _toggle_log(self) -> None: - self._log_collapsed = not self._log_collapsed - self._log_widget.setVisible(not self._log_collapsed) - self._log_last_line.setVisible(self._log_collapsed) - if self._log_collapsed: - self._vertical_splitter.setSizes([660, 36]) - self._log_toggle_btn.setText(tr("log_expand_btn")) - else: - self._vertical_splitter.setSizes([480, 220]) - self._log_toggle_btn.setText(tr("log_collapse_btn")) - - # ── Splitter persistence ── - - def _restore_splitter_sizes(self) -> None: - main_sizes = self._settings.value("mainSplitter") - if main_sizes: - try: - splitter = self._vertical_splitter.widget(0) - if isinstance(splitter, QSplitter): - splitter.setSizes([int(s) for s in main_sizes]) - except (ValueError, TypeError): - pass - - def _save_splitter_sizes(self) -> None: - splitter = self._vertical_splitter.widget(0) - if isinstance(splitter, QSplitter): - self._settings.setValue("mainSplitter", splitter.sizes()) - - def closeEvent(self, event) -> None: - self._save_splitter_sizes() - super().closeEvent(event) - - # ── Module selection ── - - def _on_module_selected(self, module: BaseModule) -> None: - if module is self._current_module: - return - if self._current_module is not None: - self._results_cache[id(self._current_module)] = self._results_table.snapshot() - self._current_module = module - cached = self._results_cache.get(id(module)) - if cached: - self._results_table.restore(cached) - else: - self._results_table.clear_results() - key = id(module) - widget = self._config_widgets.get(key) - if widget is None: - widget = module.get_config_widget() - if widget is None: - from app.ui.module_views.placeholder_view import PlaceholderView - widget = PlaceholderView(module.name) - self._config_widgets[key] = widget - self._module_stack.addWidget(widget) - self._module_stack.setCurrentWidget(widget) - - # ── Validation ── - - def _validate_start(self) -> bool: - """Check that required inputs are loaded. Show toast on failure.""" - config_widget = self._config_widgets.get(id(self._current_module)) - if config_widget is None: - return True - - # Modules that need wallets - if hasattr(config_widget, "get_wallets"): - wallets = config_widget.get_wallets() - if not wallets: - show_toast(self, tr("validation_no_wallets"), level="warning") - return False - - # Modules that need tokens (Twitter/Discord) - if hasattr(config_widget, "get_tokens"): - tokens = config_widget.get_tokens() - if not tokens: - show_toast(self, tr("validation_no_tokens"), level="warning") - return False - - # Proxy checker needs proxies - if hasattr(config_widget, "get_proxies") and not hasattr(config_widget, "get_wallets") and not hasattr(config_widget, "get_tokens"): - proxies = config_widget.get_proxies() - if not proxies: - show_toast(self, tr("validation_no_proxies"), level="warning") - return False - - return True - - # ── Start / Stop ── - - def _on_start(self) -> None: - if self._current_module is None: - return - if not self._start_btn.isEnabled(): - return - if not self._validate_start(): - return - - self._results_table.clear_results() - # Apply module column schema before any rows arrive - schema = self._current_module.column_schema() - if schema: - self._results_table.set_schema(schema) - self._start_btn.setEnabled(False) - self._stop_btn.setEnabled(True) - - self._sidebar.set_module_status(self._current_module, "running") - - self._done_count = 0 - self._total_count = self._current_module.get_item_count() - if self._total_count > 0: - self._progress_bar.setRange(0, self._total_count) - self._progress_bar.setValue(0) - self._progress_label.setText("0 / {0} (0%)".format(self._total_count)) - else: - self._progress_bar.setRange(0, 0) - self._progress_label.setText("") - self._progress_widget.setVisible(True) - - config_widget = self._config_widgets.get(id(self._current_module)) - proxies = ( - config_widget.get_proxies() - if config_widget and hasattr(config_widget, "get_proxies") - else [] - ) - ctx = RunContext( - items=[], - proxies=proxies, - rpc_urls=[], - concurrency=min(len(proxies), 50) if proxies else 10, - ) - logger = Logger(on_log_signal=self._task_runner.on_log) - ctx.extra["logger"] = logger - self._task_runner.submit(self._current_module, ctx) - - def _on_stop(self) -> None: - if self._stop_btn.isEnabled(): - self._task_runner.stop_module() - - def _on_result(self, result: Result) -> None: - self._results_table.add_row(result) - self._done_count += 1 - if self._total_count > 0: - display = min(self._done_count, self._total_count) - pct = int(display / self._total_count * 100) - self._progress_bar.setValue(display) - self._progress_label.setText(f"{display} / {self._total_count} ({pct}%)") - # Update sidebar with live count - if self._current_module: - self._sidebar.set_module_progress( - self._current_module, display, self._total_count - ) - - def _on_log(self, line: str) -> None: - self._log_widget.append(line) - # Update collapsed log preview - if self._log_collapsed: - truncated = line[:120] + "…" if len(line) > 120 else line - self._log_last_line.setText(truncated) - - def _on_finished(self) -> None: - self._start_btn.setEnabled(True) - self._stop_btn.setEnabled(False) - - cached = self._results_table.snapshot() - has_errors = any(r.status.value == "error" for r in cached) - status = "error" if has_errors else "done" - - if self._current_module: - self._sidebar.set_module_status(self._current_module, status) - - if self._total_count > 0: - self._progress_bar.setValue(self._total_count) - pct = int(self._done_count / self._total_count * 100) if self._total_count else 100 - self._progress_label.setText(f"{self._done_count} / {self._total_count} ({pct}%)") - - # Toast notification - if has_errors: - show_toast(self, tr("toast_finished_errors"), level="warning") - else: - show_toast(self, tr("toast_finished_ok"), level="success") - - # Don't auto-hide progress — keep it visible for reference - - def _quick_export(self) -> None: - """Ctrl+E — trigger XLSX export from config panel if available.""" - config_widget = self._config_widgets.get( - id(self._current_module) if self._current_module else None - ) - if config_widget and hasattr(config_widget, "_on_export"): - config_widget._on_export("xlsx") diff --git a/app/ui/module_views/__init__.py b/app/ui/module_views/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/app/ui/module_views/collector_view.py b/app/ui/module_views/collector_view.py deleted file mode 100644 index 0f026ee..0000000 --- a/app/ui/module_views/collector_view.py +++ /dev/null @@ -1,436 +0,0 @@ -# app/ui/module_views/collector_view.py -from __future__ import annotations -from dataclasses import dataclass - -from PySide6.QtWidgets import ( - QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, - QLabel, QLineEdit, QFileDialog, QPushButton, - QCheckBox, QDoubleSpinBox, QSpinBox, QScrollArea, QFrame, -) - -from app.ui.widgets.chain_picker import ChainPickerWidget -from app.ui.widgets.drop_zone import DropZone - -from app.core.models import ProxyConfig -from app.storage.parsers import parse_wallets, parse_proxies, parse_lines -from app.i18n import tr, i18n - - -@dataclass -class CollectorSettings: - min_token_usd: float # default 1.0 - min_bridge_usd: float # default 1.0 — пропустить бридж если баланс меньше - excluded_chains: list[str] # DeBank keys в lowercase - target_chains: list[str] # DeBank keys или названия сетей - slippage: float # десятичная (0.005 = 0.5%) - delay_min: int # default 10 - delay_max: int # default 30 - send_to_exchange: bool - delay_after_bridge: int # default 60 - - -class CollectorConfigWidget(QWidget): - """Панель настроек модуля Collector.""" - - def __init__(self, parent=None) -> None: - super().__init__(parent) - self._wallets: list[dict] = [] # [{"raw": ..., "type": ...}] - self._proxies: list[ProxyConfig] = [] - self._subaccounts: list[str] = [] - self._export_results: list = [] - self._export_details: dict = {} - - root_layout = QVBoxLayout(self) - root_layout.setContentsMargins(0, 0, 0, 0) - root_layout.setSpacing(0) - - scroll = QScrollArea(self) - scroll.setWidgetResizable(True) - scroll.setFrameShape(QFrame.NoFrame) - - content = QWidget() - layout = QVBoxLayout(content) - layout.setContentsMargins(16, 16, 16, 16) - layout.setSpacing(8) - - scroll.setWidget(content) - root_layout.addWidget(scroll) - - # --- Кошельки (приватные ключи / мнемоники) --- - self._lbl_files_header = QLabel() - layout.addWidget(self._lbl_files_header) - - self._wallet_drop = DropZone( - label=tr("wallets_label"), - placeholder="private_keys.txt", - ) - self._wallet_drop.file_dropped.connect(self._on_wallet_file) - layout.addWidget(self._wallet_drop) - - # --- Прокси --- - self._proxy_drop = DropZone( - label=tr("proxy_label"), - placeholder="proxy.txt", - ) - self._proxy_drop.file_dropped.connect(self._on_proxy_file) - layout.addWidget(self._proxy_drop) - - # --- Фильтры --- - self._grp_filters = QGroupBox() - fg = QVBoxLayout(self._grp_filters) - - min_row = QHBoxLayout() - self._lbl_min_value = QLabel() - min_row.addWidget(self._lbl_min_value) - self._min_token_usd = QDoubleSpinBox() - self._min_token_usd.setRange(0.01, 10000.0) - self._min_token_usd.setValue(0.03) - self._min_token_usd.setDecimals(2) - self._min_token_usd.setFixedWidth(104) - self._min_token_usd.setToolTip(tr("min_value_tooltip")) - min_row.addWidget(self._min_token_usd) - min_row.addStretch() - fg.addLayout(min_row) - - excl_row = QHBoxLayout() - self._lbl_exclude_chains = QLabel() - excl_row.addWidget(self._lbl_exclude_chains) - self._excluded_chains = ChainPickerWidget(parent=self) - excl_row.addWidget(self._excluded_chains) - fg.addLayout(excl_row) - - slippage_row = QHBoxLayout() - self._lbl_slippage = QLabel() - slippage_row.addWidget(self._lbl_slippage) - self._slippage = QDoubleSpinBox() - self._slippage.setRange(0.1, 50.0) - self._slippage.setValue(3.0) - self._slippage.setDecimals(1) - self._slippage.setFixedWidth(104) - self._slippage.setToolTip(tr("slippage_tooltip")) - slippage_row.addWidget(self._slippage) - slippage_row.addStretch() - fg.addLayout(slippage_row) - - layout.addWidget(self._grp_filters) - - # --- Бридж --- - self._grp_bridge = QGroupBox() - bg = QVBoxLayout(self._grp_bridge) - - target_row = QHBoxLayout() - self._lbl_target_chains = QLabel() - target_row.addWidget(self._lbl_target_chains) - self._target_chains = ChainPickerWidget(parent=self) - target_row.addWidget(self._target_chains) - bg.addLayout(target_row) - - min_bridge_row = QHBoxLayout() - self._lbl_min_bridge = QLabel() - min_bridge_row.addWidget(self._lbl_min_bridge) - self._min_bridge_usd = QDoubleSpinBox() - self._min_bridge_usd.setRange(0.0, 100000.0) - self._min_bridge_usd.setValue(0.03) - self._min_bridge_usd.setDecimals(2) - self._min_bridge_usd.setFixedWidth(104) - self._min_bridge_usd.setToolTip(tr("min_bridge_tooltip")) - min_bridge_row.addWidget(self._min_bridge_usd) - min_bridge_row.addStretch() - bg.addLayout(min_bridge_row) - - layout.addWidget(self._grp_bridge) - - # --- Задержки --- - self._grp_delays = QGroupBox() - dg = QVBoxLayout(self._grp_delays) - - delay_row = QHBoxLayout() - self._lbl_delay_between_wallets = QLabel() - delay_row.addWidget(self._lbl_delay_between_wallets) - self._delay_min = QSpinBox() - self._delay_min.setRange(0, 3600) - self._delay_min.setValue(60) - self._delay_min.setFixedWidth(86) - self._delay_min.setToolTip(tr("delay_min_tooltip")) - delay_row.addWidget(self._delay_min) - delay_row.addWidget(QLabel("—")) - self._delay_max = QSpinBox() - self._delay_max.setRange(0, 3600) - self._delay_max.setValue(180) - self._delay_max.setFixedWidth(86) - self._delay_max.setToolTip(tr("delay_max_tooltip")) - delay_row.addWidget(self._delay_max) - delay_row.addStretch() - dg.addLayout(delay_row) - - layout.addWidget(self._grp_delays) - - # --- Субаккаунт биржи --- - self._grp_exchange = QGroupBox() - eg = QVBoxLayout(self._grp_exchange) - - self._send_to_exchange = QCheckBox() - self._send_to_exchange.toggled.connect(self._on_exchange_toggled) - eg.addWidget(self._send_to_exchange) - - self._sub_drop = DropZone( - label=tr("subaccounts_label"), - placeholder="subaccounts.txt", - ) - self._sub_drop.file_dropped.connect(self._on_subaccount_file) - self._sub_drop.setEnabled(False) - eg.addWidget(self._sub_drop) - - delay_bridge_row = QHBoxLayout() - self._lbl_after_bridge_delay = QLabel() - delay_bridge_row.addWidget(self._lbl_after_bridge_delay) - self._delay_after_bridge = QSpinBox() - self._delay_after_bridge.setRange(0, 3600) - self._delay_after_bridge.setValue(60) - self._delay_after_bridge.setFixedWidth(86) - self._delay_after_bridge.setEnabled(False) - self._delay_after_bridge.setToolTip(tr("after_bridge_delay_tooltip")) - delay_bridge_row.addWidget(self._delay_after_bridge) - delay_bridge_row.addStretch() - eg.addLayout(delay_bridge_row) - - layout.addWidget(self._grp_exchange) - - # --- Экспорт --- - self._export_group = QGroupBox() - self._grp_export = self._export_group - self._export_group.setEnabled(False) - exp_layout = QHBoxLayout(self._export_group) - self._btn_csv = QPushButton() - self._btn_json = QPushButton() - self._btn_xlsx = QPushButton() - self._btn_csv.clicked.connect(lambda: self._on_export("csv")) - self._btn_json.clicked.connect(lambda: self._on_export("json")) - self._btn_xlsx.clicked.connect(lambda: self._on_export("xlsx")) - exp_layout.addWidget(self._btn_csv) - exp_layout.addWidget(self._btn_json) - exp_layout.addWidget(self._btn_xlsx) - layout.addWidget(self._export_group) - - layout.addStretch() - - i18n.language_changed.connect(self.retranslate_ui) - self.retranslate_ui() - - def retranslate_ui(self) -> None: - self._lbl_files_header.setText(tr("files_header")) - self._wallet_drop.set_label(tr("wallets_label")) - self._proxy_drop.set_label(tr("proxy_label")) - self._grp_filters.setTitle(tr("filters_group")) - self._lbl_min_value.setText(tr("min_value_label")) - self._lbl_exclude_chains.setText(tr("exclude_chains_label")) - self._lbl_slippage.setText(tr("slippage_label")) - self._grp_bridge.setTitle(tr("bridge_group")) - self._lbl_target_chains.setText(tr("target_chains_label")) - self._lbl_min_bridge.setText(tr("min_value_label")) - self._grp_delays.setTitle(tr("delays_group")) - self._lbl_delay_between_wallets.setText(tr("delay_between_wallets_label")) - self._grp_exchange.setTitle(tr("exchange_group")) - self._send_to_exchange.setText(tr("send_to_exchange_checkbox")) - self._sub_drop.set_label(tr("subaccounts_label")) - self._lbl_after_bridge_delay.setText(tr("after_bridge_delay_label")) - self._grp_export.setTitle(tr("export_group")) - self._btn_csv.setText(tr("save_csv")) - self._btn_json.setText(tr("save_json")) - self._btn_xlsx.setText(tr("save_xlsx")) - - # --- Загрузка файлов --- - - def _on_wallet_file(self, path: str) -> None: - try: - parsed = parse_wallets(path) - self._wallets = [w for w in parsed if w["type"] in ("private_key", "mnemonic")] - self._wallet_drop.set_status(tr("n_wallets_loaded").format(n=len(self._wallets))) - self._check_wallet_subaccount_match() - except Exception as e: - self._wallets = [] - self._wallet_drop.set_status(tr("error_fmt").format(e=e)) - - def _on_proxy_file(self, path: str) -> None: - try: - self._proxies = parse_proxies(path) - self._proxy_drop.set_status(tr("n_proxies_loaded").format(n=len(self._proxies))) - except Exception as e: - self._proxies = [] - self._proxy_drop.set_status(tr("error_fmt").format(e=e)) - - def _on_subaccount_file(self, path: str) -> None: - import re - try: - lines = parse_lines(path) - invalid = [l for l in lines if not re.fullmatch(r"0x[0-9a-fA-F]{40}", l)] - if invalid: - self._subaccounts = [] - self._sub_drop.set_status( - tr("error_fmt").format(e=f"Invalid EVM addresses: {', '.join(invalid[:3])}{'…' if len(invalid) > 3 else ''}") - ) - return - self._subaccounts = lines - self._check_wallet_subaccount_match() - except Exception as e: - self._subaccounts = [] - self._sub_drop.set_status(tr("error_fmt").format(e=e)) - - def _check_wallet_subaccount_match(self) -> None: - """Показывает предупреждение если кол-во кошельков != субаккаунтов.""" - if not self._send_to_exchange.isChecked(): - return - w = len(self._wallets) - s = len(self._subaccounts) - if w == 0 or s == 0: - self._sub_drop.set_status("") - return - if w != s: - self._sub_drop.set_status(tr("wallets_subaccounts_mismatch").format(w=w, s=s)) - else: - self._sub_drop.set_status(tr("n_subaccounts_loaded").format(s=s)) - - def _on_exchange_toggled(self, checked: bool) -> None: - self._sub_drop.setEnabled(checked) - self._delay_after_bridge.setEnabled(checked) - self._check_wallet_subaccount_match() - - # --- Публичные методы --- - - def get_wallets(self) -> list[dict]: - return self._wallets - - def get_proxies(self) -> list[ProxyConfig]: - return self._proxies - - def get_subaccounts(self) -> list[str]: - return self._subaccounts - - def get_settings(self) -> CollectorSettings: - excluded = self._excluded_chains.get_selected() - target = self._target_chains.get_selected() - return CollectorSettings( - min_token_usd=self._min_token_usd.value(), - min_bridge_usd=self._min_bridge_usd.value(), - excluded_chains=excluded, - target_chains=target, - slippage=self._slippage.value() / 100.0, - delay_min=self._delay_min.value(), - delay_max=self._delay_max.value(), - send_to_exchange=self._send_to_exchange.isChecked(), - delay_after_bridge=self._delay_after_bridge.value(), - ) - - def on_run_complete(self, results: list, details: dict) -> None: - """Слот: активирует экспорт после завершения run().""" - self._export_results = results - self._export_details = details - self._export_group.setEnabled(True) - - def _on_export(self, fmt: str) -> None: - if not self._export_results: - return - extensions = { - "csv": (tr("save_csv"), "collector_results.csv", "CSV (*.csv)"), - "json": (tr("save_json"), "collector_results.json", "JSON (*.json)"), - "xlsx": (tr("save_xlsx"), "collector_results.xlsx", "Excel (*.xlsx)"), - } - title, default_name, file_filter = extensions[fmt] - path, _ = QFileDialog.getSaveFileName(self, title, default_name, file_filter) - if not path: - return - import json, csv - - # Build summary rows (bridge_ops excluded — shown in separate Bridges sheet/section) - summary_rows = [] - bridge_rows = [] - for r in self._export_results: - data = dict(r.data) - ops = data.pop("bridge_ops", []) or [] - bridge_count = len(ops) - summary_rows.append({"item": r.item, "status": r.status.value, - "bridges": bridge_count, **data}) - for op in ops: - bridge_rows.append({ - "address": r.item, - "src_chain": op.get("src", ""), - "tgt_chain": op.get("tgt", ""), - "tx_hash": op.get("tx", ""), - "status": op.get("status", ""), - "sent_usd": op.get("usd", 0), - }) - - total_usd = sum(r.data.get("total_collected_usd", 0) or 0 for r in self._export_results) - - if fmt == "json": - with open(path, "w", encoding="utf-8") as f: - json.dump({ - "total_collected_usd": round(total_usd, 2), - "summary": summary_rows, - "bridges": bridge_rows, - }, f, ensure_ascii=False, indent=2) - - elif fmt == "csv": - if summary_rows: - with open(path, "w", newline="", encoding="utf-8-sig") as f: - writer = csv.DictWriter(f, fieldnames=summary_rows[0].keys()) - writer.writeheader() - writer.writerows(summary_rows) - writer.writerow({}) - writer.writerow({"item": "TOTAL", "total_collected_usd": round(total_usd, 2)}) - if bridge_rows: - writer.writerow({}) - writer.writerow({"item": "=== BRIDGES ==="}) - with open(path, "a", newline="", encoding="utf-8-sig") as f: - if bridge_rows: - writer = csv.DictWriter(f, fieldnames=bridge_rows[0].keys()) - writer.writeheader() - writer.writerows(bridge_rows) - - elif fmt == "xlsx": - try: - import openpyxl - from openpyxl.styles import Font, PatternFill, Alignment - except ImportError: - import subprocess, sys - subprocess.check_call([sys.executable, "-m", "pip", "install", "openpyxl"]) - import openpyxl - from openpyxl.styles import Font, PatternFill, Alignment - wb = openpyxl.Workbook() - - # ── Sheet 1: Summary ────────────────────────────────────────── - ws1 = wb.active - ws1.title = "Summary" - if summary_rows: - header = list(summary_rows[0].keys()) - ws1.append(header) - for cell in ws1[1]: - cell.font = Font(bold=True) - for row in summary_rows: - ws1.append(list(row.values())) - ws1.append([]) - total_row = ["TOTAL"] + [""] * (len(header) - 1) - if "total_collected_usd" in header: - total_row[header.index("total_collected_usd")] = round(total_usd, 2) - ws1.append(total_row) - for cell in ws1[ws1.max_row]: - cell.font = Font(bold=True) - - # ── Sheet 2: Bridges ───────────────────────────────────────── - ws2 = wb.create_sheet("Bridges") - if bridge_rows: - b_header = list(bridge_rows[0].keys()) - ws2.append(b_header) - for cell in ws2[1]: - cell.font = Font(bold=True) - for row in bridge_rows: - ws2.append(list(row.values())) - - try: - wb.save(path) - except PermissionError: - from PySide6.QtWidgets import QMessageBox - QMessageBox.warning( - self, tr("save_error_title"), - tr("save_error_msg").format(path=path), - ) diff --git a/app/ui/module_views/discord_checker_view.py b/app/ui/module_views/discord_checker_view.py deleted file mode 100644 index 77c070a..0000000 --- a/app/ui/module_views/discord_checker_view.py +++ /dev/null @@ -1,134 +0,0 @@ -from __future__ import annotations -from PySide6.QtWidgets import ( - QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, - QLabel, QFileDialog, QComboBox, QPushButton, -) -from app.ui.widgets.drop_zone import DropZone -from app.core.models import ProxyConfig -from app.storage.parsers import parse_proxies, parse_lines -from app.i18n import tr, i18n - - -class DiscordCheckerConfigWidget(QWidget): - """Config panel for Discord Checker module.""" - - def __init__(self, parent=None) -> None: - super().__init__(parent) - self._tokens: list[str] = [] - self._proxies: list[ProxyConfig] = [] - self._export_results: list = [] - - layout = QVBoxLayout(self) - layout.setContentsMargins(16, 16, 16, 16) - layout.setSpacing(8) - - # --- Tokens --- - self._token_drop = DropZone( - label=tr("tokens_label_checker"), - placeholder="tokens.txt", - ) - self._token_drop.file_dropped.connect(self._on_token_file) - layout.addWidget(self._token_drop) - - # --- Proxies --- - self._proxy_drop = DropZone( - label=tr("proxy_label_checker"), - placeholder="proxy.txt", - ) - self._proxy_drop.file_dropped.connect(self._on_proxy_file) - layout.addWidget(self._proxy_drop) - - # --- Export (disabled until run completes) --- - self._export_group = QGroupBox() - self._export_group.setEnabled(False) - export_layout = QVBoxLayout(self._export_group) - - filter_row = QHBoxLayout() - self._lbl_status = QLabel() - filter_row.addWidget(self._lbl_status) - self._status_combo = QComboBox() - self._status_combo.addItem(tr("filter_all"), None) - for s in ("ok", "invalid", "disabled"): - self._status_combo.addItem(s, s) - filter_row.addWidget(self._status_combo) - filter_row.addSpacing(16) - self._lbl_format = QLabel() - filter_row.addWidget(self._lbl_format) - self._fmt_combo = QComboBox() - for fmt in ("xlsx", "csv", "json"): - self._fmt_combo.addItem(fmt, fmt) - filter_row.addWidget(self._fmt_combo) - filter_row.addStretch() - export_layout.addLayout(filter_row) - - self._export_btn = QPushButton() - self._export_btn.clicked.connect(self._on_export) - export_layout.addWidget(self._export_btn) - - layout.addWidget(self._export_group) - layout.addStretch() - - self.retranslate_ui() - i18n.language_changed.connect(self.retranslate_ui) - - def retranslate_ui(self) -> None: - self._token_drop.set_label(tr("tokens_label_checker")) - self._proxy_drop.set_label(tr("proxy_label_checker")) - self._export_group.setTitle(tr("export_group")) - self._lbl_status.setText(tr("status_label")) - self._lbl_format.setText(tr("format_label")) - self._export_btn.setText(tr("export_btn")) - self._status_combo.setItemText(0, tr("filter_all")) - - # --- File loading --- - - def _on_token_file(self, path: str) -> None: - try: - self._tokens = parse_lines(path) - self._token_drop.set_status(tr("n_tokens_loaded").format(n=len(self._tokens))) - except Exception as e: - self._tokens = [] - self._token_drop.set_status(tr("error_fmt").format(e=e)) - - def _on_proxy_file(self, path: str) -> None: - try: - self._proxies = parse_proxies(path) - self._proxy_drop.set_status(tr("n_proxies_loaded").format(n=len(self._proxies))) - except Exception as e: - self._proxies = [] - self._proxy_drop.set_status(tr("error_fmt").format(e=e)) - - # --- Accessors (called by module) --- - - def get_tokens(self) -> list[str]: - return self._tokens - - def get_proxies(self) -> list[ProxyConfig]: - return self._proxies - - def on_run_complete(self, results: list) -> None: - """Slot: called via Qt queued connection after run() finishes.""" - self._export_results = results - self._export_group.setEnabled(True) - - # --- Export --- - - def _on_export(self) -> None: - if not self._export_results: - return - from app.storage.discord_exporter import DiscordExporter, DiscordExportConfig - - status_filter = self._status_combo.currentData() - fmt = self._fmt_combo.currentData() - config = DiscordExportConfig(status_filter=status_filter) - - extensions = { - "csv": (tr("save_csv"), "discord_results.csv", "CSV (*.csv)"), - "json": (tr("save_json"), "discord_results.json", "JSON (*.json)"), - "xlsx": (tr("save_xlsx"), "discord_results.xlsx", "Excel (*.xlsx)"), - } - title, default_name, file_filter = extensions[fmt] - path, _ = QFileDialog.getSaveFileName(self, title, default_name, file_filter) - if not path: - return - DiscordExporter().export(self._export_results, config, path, fmt) diff --git a/app/ui/module_views/evm_balance_view.py b/app/ui/module_views/evm_balance_view.py deleted file mode 100644 index 2504872..0000000 --- a/app/ui/module_views/evm_balance_view.py +++ /dev/null @@ -1,161 +0,0 @@ -from __future__ import annotations -from PySide6.QtWidgets import ( - QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, - QLabel, QFileDialog, QCheckBox, QComboBox, QPushButton, -) -from app.ui.widgets.drop_zone import DropZone -from app.core.models import ProxyConfig -from app.storage.parsers import parse_wallets, parse_proxies -from app.i18n import tr, i18n - - -class EvmBalanceConfigWidget(QWidget): - """Панель настроек модуля EVM Balance: файлы + секция экспорта.""" - - def __init__(self, parent=None) -> None: - super().__init__(parent) - self._wallets: list[str] = [] - self._proxies: list[ProxyConfig] = [] - self._export_results: list = [] - self._export_details: dict = {} - - layout = QVBoxLayout(self) - layout.setContentsMargins(16, 16, 16, 16) - layout.setSpacing(8) - - # --- Кошельки --- - self._wallet_drop = DropZone( - label=tr("wallets_label"), - placeholder="wallets.txt", - ) - self._wallet_drop.file_dropped.connect(self._on_wallet_file) - layout.addWidget(self._wallet_drop) - - # --- Прокси --- - self._proxy_drop = DropZone( - label=tr("proxy_label"), - placeholder="proxy.txt", - ) - self._proxy_drop.file_dropped.connect(self._on_proxy_file) - layout.addWidget(self._proxy_drop) - - # --- Секция экспорта (выключена до завершения проверки) --- - self._export_group = QGroupBox() - self._export_group.setEnabled(False) - export_layout = QVBoxLayout(self._export_group) - - # Галочки секций (Summary экспортируется из таблицы результатов слева) - cb_row = QHBoxLayout() - self._cb_tokens = QCheckBox() - for cb in (self._cb_tokens,): - cb.setChecked(True) - cb_row.addWidget(cb) - export_layout.addLayout(cb_row) - - # Фильтр токенов — выпадающий список (заполняется после проверки) - token_row = QHBoxLayout() - self._lbl_tokens_filter = QLabel() - token_row.addWidget(self._lbl_tokens_filter) - self._filter_tokens = QComboBox() - self._filter_tokens.setEditable(False) - self._filter_tokens.setMinimumWidth(160) - token_row.addWidget(self._filter_tokens) - export_layout.addLayout(token_row) - - # Кнопки экспорта - btn_row = QHBoxLayout() - self._btn_csv = QPushButton("CSV") - self._btn_json = QPushButton("JSON") - self._btn_xlsx = QPushButton("XLSX") - self._btn_csv.clicked.connect(lambda: self._on_export("csv")) - self._btn_json.clicked.connect(lambda: self._on_export("json")) - self._btn_xlsx.clicked.connect(lambda: self._on_export("xlsx")) - btn_row.addWidget(self._btn_csv) - btn_row.addWidget(self._btn_json) - btn_row.addWidget(self._btn_xlsx) - export_layout.addLayout(btn_row) - - layout.addWidget(self._export_group) - layout.addStretch() - - self.retranslate_ui() - i18n.language_changed.connect(self.retranslate_ui) - - def retranslate_ui(self) -> None: - self._wallet_drop.set_label(tr("wallets_label")) - self._proxy_drop.set_label(tr("proxy_label")) - self._export_group.setTitle(tr("export_group")) - self._cb_tokens.setText(tr("tokens_checkbox")) - self._lbl_tokens_filter.setText(tr("tokens_filter_label")) - - # --- Загрузка файлов --- - - def _on_wallet_file(self, path: str) -> None: - try: - parsed = parse_wallets(path) - self._wallets = [w["raw"] for w in parsed if w["type"] == "address"] - self._wallet_drop.set_status(tr("n_addresses_loaded").format(n=len(self._wallets))) - except Exception as e: - self._wallets = [] - self._wallet_drop.set_status(tr("error_fmt").format(e=e)) - - def _on_proxy_file(self, path: str) -> None: - try: - self._proxies = parse_proxies(path) - self._proxy_drop.set_status(tr("n_proxies_loaded").format(n=len(self._proxies))) - except Exception as e: - self._proxies = [] - self._proxy_drop.set_status(tr("error_fmt").format(e=e)) - - def get_wallets(self) -> list[str]: - return self._wallets - - def get_proxies(self) -> list[ProxyConfig]: - return self._proxies - - # --- Экспорт --- - - def on_run_complete(self, results: list, details: dict) -> None: - """Слот: вызывается Qt через queued connection после завершения run().""" - self._export_results = results - self._export_details = details - self._export_group.setEnabled(True) - # Собираем уникальные токены из кошельков для выпадающего списка - token_keys: set[tuple[str, str]] = set() - for addr_detail in details.values(): - for t in addr_detail.get("tokens_data", []): - sym = (t.get("symbol") or "?").strip() - ch = (t.get("chain") or "?").strip() - if sym and ch: - token_keys.add((sym, ch)) - self._filter_tokens.clear() - self._filter_tokens.addItem(tr("all_tokens"), None) - for sym, ch in sorted(token_keys): - display = f"{sym} ({ch.capitalize()})" - value = f"{sym}:{ch}" - self._filter_tokens.addItem(display, value) - - def _build_config(self): - from app.storage.evm_exporter import EvmExportConfig - token_filter = self._filter_tokens.currentData() - return EvmExportConfig( - summary=False, - tokens=self._cb_tokens.isChecked(), - token_filter=token_filter, - ) - - def _on_export(self, fmt: str) -> None: - if not self._export_results: - return - config = self._build_config() - extensions = { - "csv": (tr("save_csv"), "evm_results.csv", "CSV (*.csv)"), - "json": (tr("save_json"), "evm_results.json", "JSON (*.json)"), - "xlsx": (tr("save_xlsx"), "evm_results.xlsx", "Excel (*.xlsx)"), - } - title, default_name, file_filter = extensions[fmt] - path, _ = QFileDialog.getSaveFileName(self, title, default_name, file_filter) - if not path: - return - from app.storage.evm_exporter import EvmExporter - EvmExporter().export(self._export_results, self._export_details, config, path, fmt) diff --git a/app/ui/module_views/placeholder_view.py b/app/ui/module_views/placeholder_view.py deleted file mode 100644 index 2d37836..0000000 --- a/app/ui/module_views/placeholder_view.py +++ /dev/null @@ -1,20 +0,0 @@ -from PySide6.QtWidgets import QLabel, QVBoxLayout, QWidget -from PySide6.QtCore import Qt -from app.i18n import tr, i18n - - -class PlaceholderView(QWidget): - """Заглушка панели настроек для модулей без реализации.""" - - def __init__(self, module_name: str, parent=None) -> None: - super().__init__(parent) - self._module_name = module_name - self._label = QLabel() - self._label.setAlignment(Qt.AlignCenter) - layout = QVBoxLayout(self) - layout.addWidget(self._label) - i18n.language_changed.connect(self.retranslate_ui) - self.retranslate_ui() - - def retranslate_ui(self) -> None: - self._label.setText(tr("placeholder_module_msg").format(module_name=self._module_name)) diff --git a/app/ui/module_views/proxy_checker_view.py b/app/ui/module_views/proxy_checker_view.py deleted file mode 100644 index ffca9d3..0000000 --- a/app/ui/module_views/proxy_checker_view.py +++ /dev/null @@ -1,45 +0,0 @@ -from __future__ import annotations -from PySide6.QtWidgets import QWidget, QVBoxLayout -from app.ui.widgets.drop_zone import DropZone -from app.core.models import ProxyConfig -from app.storage.parsers import parse_proxies -from app.i18n import tr, i18n - - -class ProxyCheckerConfigWidget(QWidget): - """Панель настроек модуля Proxy Checker: выбор файла прокси.""" - - def __init__(self, parent=None) -> None: - super().__init__(parent) - self._proxies: list[ProxyConfig] = [] - - layout = QVBoxLayout(self) - layout.setContentsMargins(16, 16, 16, 16) - layout.setSpacing(8) - - self._proxy_drop = DropZone( - label=tr("proxy_label"), - placeholder="proxies.txt", - ) - self._proxy_drop.file_dropped.connect(self._on_proxy_file) - layout.addWidget(self._proxy_drop) - - layout.addStretch() - - self.retranslate_ui() - i18n.language_changed.connect(self.retranslate_ui) - - def retranslate_ui(self) -> None: - self._proxy_drop.set_label(tr("proxy_label")) - - def _on_proxy_file(self, path: str) -> None: - try: - self._proxies = parse_proxies(path) - self._proxy_drop.set_status(tr("n_proxies_loaded").format(n=len(self._proxies))) - except Exception as e: - self._proxies = [] - self._proxy_drop.set_status(tr("error_fmt").format(e=e)) - - def get_proxies(self) -> list[ProxyConfig]: - """Возвращает список прокси.""" - return self._proxies diff --git a/app/ui/module_views/svm_balance_view.py b/app/ui/module_views/svm_balance_view.py deleted file mode 100644 index 08d5f12..0000000 --- a/app/ui/module_views/svm_balance_view.py +++ /dev/null @@ -1,165 +0,0 @@ -from __future__ import annotations - -from PySide6.QtWidgets import ( - QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, - QLabel, QLineEdit, QFileDialog, QComboBox, QPushButton, -) - -from app.ui.widgets.drop_zone import DropZone -from app.core.models import ProxyConfig, Result -from app.storage.parsers import parse_lines, parse_proxies -from app.i18n import tr, i18n - -DEFAULT_RPC_URL = "https://api.mainnet-beta.solana.com" - - -class SvmBalanceConfigWidget(QWidget): - """Config panel for the SVM Balance module: file inputs + export section.""" - - def __init__(self, parent=None) -> None: - super().__init__(parent) - self._wallets: list[str] = [] - self._proxies: list[ProxyConfig] = [] - self._export_results: list[Result] = [] - self._export_details: dict = {} - - layout = QVBoxLayout(self) - layout.setContentsMargins(16, 16, 16, 16) - layout.setSpacing(8) - - # --- Кошельки --- - self._wallet_drop = DropZone( - label=tr("wallets_label"), - placeholder="wallets.txt", - ) - self._wallet_drop.file_dropped.connect(self._on_wallet_file) - layout.addWidget(self._wallet_drop) - - # --- Прокси --- - self._proxy_drop = DropZone( - label=tr("proxy_label"), - placeholder="proxy.txt", - ) - self._proxy_drop.file_dropped.connect(self._on_proxy_file) - layout.addWidget(self._proxy_drop) - - # --- RPC URL --- - self._lbl_rpc = QLabel() - layout.addWidget(self._lbl_rpc) - self._rpc_url_input = QLineEdit(DEFAULT_RPC_URL) - layout.addWidget(self._rpc_url_input) - - # --- Экспорт --- - self._export_group = QGroupBox() - self._export_group.setEnabled(False) - export_layout = QVBoxLayout(self._export_group) - - token_row = QHBoxLayout() - self._lbl_tokens = QLabel() - token_row.addWidget(self._lbl_tokens) - self._filter_tokens = QComboBox() - self._filter_tokens.setEditable(False) - self._filter_tokens.setMinimumWidth(160) - token_row.addWidget(self._filter_tokens) - export_layout.addLayout(token_row) - - btn_row = QHBoxLayout() - self._btn_csv = QPushButton("CSV") - self._btn_json = QPushButton("JSON") - self._btn_xlsx = QPushButton("XLSX") - self._btn_csv.clicked.connect(lambda: self._on_export("csv")) - self._btn_json.clicked.connect(lambda: self._on_export("json")) - self._btn_xlsx.clicked.connect(lambda: self._on_export("xlsx")) - btn_row.addWidget(self._btn_csv) - btn_row.addWidget(self._btn_json) - btn_row.addWidget(self._btn_xlsx) - export_layout.addLayout(btn_row) - - layout.addWidget(self._export_group) - layout.addStretch() - - self.retranslate_ui() - i18n.language_changed.connect(self.retranslate_ui) - - def retranslate_ui(self) -> None: - self._wallet_drop.set_label(tr("wallets_label")) - self._proxy_drop.set_label(tr("proxy_label")) - self._lbl_rpc.setText(tr("rpc_url_label")) - self._export_group.setTitle(tr("export_group")) - self._lbl_tokens.setText(tr("tokens_filter_label")) - - # --- File loading --- - - def _on_wallet_file(self, path: str) -> None: - try: - self._wallets = parse_lines(path) - self._wallet_drop.set_status(tr("n_addresses_loaded").format(n=len(self._wallets))) - except Exception as e: - self._wallets = [] - self._wallet_drop.set_status(tr("error_fmt").format(e=e)) - - def _on_proxy_file(self, path: str) -> None: - try: - self._proxies = parse_proxies(path) - self._proxy_drop.set_status(tr("n_proxies_loaded").format(n=len(self._proxies))) - except Exception as e: - self._proxies = [] - self._proxy_drop.set_status(tr("error_fmt").format(e=e)) - - # --- Accessors --- - - def get_wallets(self) -> list[str]: - return self._wallets - - def get_proxies(self) -> list[ProxyConfig]: - return self._proxies - - def get_rpc_url(self) -> str: - return self._rpc_url_input.text().strip() or DEFAULT_RPC_URL - - # --- Run complete slot --- - - def on_run_complete(self, results: list[Result], details: dict) -> None: - """Called via Qt queued connection after module.run() finishes.""" - self._export_results = results - self._export_details = details - self._export_group.setEnabled(True) - - # Populate token filter combobox with unique symbols - symbols: set[str] = set() - for addr_detail in details.values(): - for t in addr_detail.get("tokens_data", []): - sym = (t.get("symbol") or "").strip() - if sym: - symbols.add(sym) - - self._filter_tokens.clear() - self._filter_tokens.addItem(tr("all_tokens"), None) - for sym in sorted(symbols): - self._filter_tokens.addItem(sym, sym) - - # --- Export --- - - def _build_config(self): - from app.storage.svm_exporter import SvmExportConfig - return SvmExportConfig( - summary=True, - tokens=True, - token_filter=self._filter_tokens.currentData(), - ) - - def _on_export(self, fmt: str) -> None: - if not self._export_results: - return - config = self._build_config() - extensions = { - "csv": (tr("save_csv"), "svm_results.csv", "CSV (*.csv)"), - "json": (tr("save_json"), "svm_results.json", "JSON (*.json)"), - "xlsx": (tr("save_xlsx"), "svm_results.xlsx", "Excel (*.xlsx)"), - } - title, default_name, file_filter = extensions[fmt] - path, _ = QFileDialog.getSaveFileName(self, title, default_name, file_filter) - if not path: - return - from app.storage.svm_exporter import SvmExporter - SvmExporter().export(self._export_results, self._export_details, config, path, fmt) diff --git a/app/ui/module_views/twitter_checker_view.py b/app/ui/module_views/twitter_checker_view.py deleted file mode 100644 index d3edf92..0000000 --- a/app/ui/module_views/twitter_checker_view.py +++ /dev/null @@ -1,134 +0,0 @@ -from __future__ import annotations -from PySide6.QtWidgets import ( - QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, - QLabel, QFileDialog, QComboBox, QPushButton, -) -from app.ui.widgets.drop_zone import DropZone -from app.core.models import ProxyConfig -from app.storage.parsers import parse_proxies, parse_lines -from app.i18n import tr, i18n - - -class TwitterCheckerConfigWidget(QWidget): - """Config panel for Twitter Checker module.""" - - def __init__(self, parent=None) -> None: - super().__init__(parent) - self._tokens: list[str] = [] - self._proxies: list[ProxyConfig] = [] - self._export_results: list = [] - - layout = QVBoxLayout(self) - layout.setContentsMargins(16, 16, 16, 16) - layout.setSpacing(8) - - # --- Tokens --- - self._token_drop = DropZone( - label=tr("tokens_label_checker"), - placeholder="tokens.txt", - ) - self._token_drop.file_dropped.connect(self._on_token_file) - layout.addWidget(self._token_drop) - - # --- Proxies --- - self._proxy_drop = DropZone( - label=tr("proxy_label_checker"), - placeholder="proxy.txt", - ) - self._proxy_drop.file_dropped.connect(self._on_proxy_file) - layout.addWidget(self._proxy_drop) - - # --- Export (disabled until run completes) --- - self._export_group = QGroupBox() - self._export_group.setEnabled(False) - export_layout = QVBoxLayout(self._export_group) - - filter_row = QHBoxLayout() - self._lbl_status = QLabel() - filter_row.addWidget(self._lbl_status) - self._status_combo = QComboBox() - self._status_combo.addItem(tr("filter_all"), None) - for s in ("ok", "invalid", "suspended", "locked"): - self._status_combo.addItem(s, s) - filter_row.addWidget(self._status_combo) - filter_row.addSpacing(16) - self._lbl_format = QLabel() - filter_row.addWidget(self._lbl_format) - self._fmt_combo = QComboBox() - for fmt in ("xlsx", "csv", "json"): - self._fmt_combo.addItem(fmt, fmt) - filter_row.addWidget(self._fmt_combo) - filter_row.addStretch() - export_layout.addLayout(filter_row) - - self._export_btn = QPushButton() - self._export_btn.clicked.connect(self._on_export) - export_layout.addWidget(self._export_btn) - - layout.addWidget(self._export_group) - layout.addStretch() - - self.retranslate_ui() - i18n.language_changed.connect(self.retranslate_ui) - - def retranslate_ui(self) -> None: - self._token_drop.set_label(tr("tokens_label_checker")) - self._proxy_drop.set_label(tr("proxy_label_checker")) - self._export_group.setTitle(tr("export_group")) - self._lbl_status.setText(tr("status_label")) - self._lbl_format.setText(tr("format_label")) - self._export_btn.setText(tr("export_btn")) - self._status_combo.setItemText(0, tr("filter_all")) - - # --- File loading --- - - def _on_token_file(self, path: str) -> None: - try: - self._tokens = parse_lines(path) - self._token_drop.set_status(tr("n_tokens_loaded").format(n=len(self._tokens))) - except Exception as e: - self._tokens = [] - self._token_drop.set_status(tr("error_fmt").format(e=e)) - - def _on_proxy_file(self, path: str) -> None: - try: - self._proxies = parse_proxies(path) - self._proxy_drop.set_status(tr("n_proxies_loaded").format(n=len(self._proxies))) - except Exception as e: - self._proxies = [] - self._proxy_drop.set_status(tr("error_fmt").format(e=e)) - - # --- Accessors (called by module) --- - - def get_tokens(self) -> list[str]: - return self._tokens - - def get_proxies(self) -> list[ProxyConfig]: - return self._proxies - - def on_run_complete(self, results: list) -> None: - """Slot: called via Qt queued connection after run() finishes.""" - self._export_results = results - self._export_group.setEnabled(True) - - # --- Export --- - - def _on_export(self) -> None: - if not self._export_results: - return - from app.storage.twitter_exporter import TwitterExporter, TwitterExportConfig - - status_filter = self._status_combo.currentData() - fmt = self._fmt_combo.currentData() - config = TwitterExportConfig(status_filter=status_filter) - - extensions = { - "csv": (tr("save_csv"), "twitter_results.csv", "CSV (*.csv)"), - "json": (tr("save_json"), "twitter_results.json", "JSON (*.json)"), - "xlsx": (tr("save_xlsx"), "twitter_results.xlsx", "Excel (*.xlsx)"), - } - title, default_name, file_filter = extensions[fmt] - path, _ = QFileDialog.getSaveFileName(self, title, default_name, file_filter) - if not path: - return - TwitterExporter().export(self._export_results, config, path, fmt) diff --git a/app/ui/results_table.py b/app/ui/results_table.py deleted file mode 100644 index a509e18..0000000 --- a/app/ui/results_table.py +++ /dev/null @@ -1,232 +0,0 @@ -from __future__ import annotations - -from PySide6.QtWidgets import ( - QWidget, QVBoxLayout, QHBoxLayout, QTableWidget, QTableWidgetItem, - QLineEdit, QPushButton, QHeaderView, QLabel, QComboBox, QStackedWidget, -) -from PySide6.QtCore import Qt -from PySide6.QtGui import QColor - -from app.core.models import Result, ResultStatus -from app.storage.exporters import get_columns, CsvExporter, JsonExporter, XlsxExporter -from app.ui.widgets.empty_state import EmptyState -from app.i18n import tr, i18n - -# Status colors -_STATUS_COLORS = { - "ok": QColor("#30D158"), - "error": QColor("#FF453A"), - "skip": QColor("#F0C040"), -} - -_STATUS_BG = { - "ok": QColor(48, 209, 88, 25), - "error": QColor(255, 69, 58, 25), - "skip": QColor(240, 192, 64, 25), -} - -_ALT_ROW_COLOR = QColor(255, 255, 255, 6) - - -class ResultsTable(QWidget): - """Таблица результатов с поиском, фильтром, статус-бейджами и empty state.""" - - def __init__(self, parent=None) -> None: - super().__init__(parent) - self._results: list[Result] = [] - self._columns: list[str] = ["item", "status"] - - # Search bar - self._search = QLineEdit() - self._search.setObjectName("resultsSearch") - self._search.setClearButtonEnabled(True) - self._search.textChanged.connect(self._apply_filter) - - # Table - self._table = QTableWidget() - self._table.setAlternatingRowColors(True) - self._table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) - self._table.verticalHeader().setVisible(False) - self._table.setSelectionBehavior(QTableWidget.SelectRows) - self._table.setShowGrid(False) - - # Empty state - self._empty = EmptyState( - icon_text="📋", - title=tr("empty_results_title"), - subtitle=tr("empty_results_subtitle"), - ) - - # Stacked: empty ↔ table - self._stack = QStackedWidget() - self._stack.addWidget(self._empty) # index 0 - self._stack.addWidget(self._table) # index 1 - self._stack.setCurrentIndex(0) - - # Quality filter - self._quality_filter = QComboBox() - self._quality_filter.addItem(tr("filter_all"), "all") - for q in ("high", "medium", "low"): - self._quality_filter.addItem(q, q) - self._quality_filter.setVisible(False) - - # Export buttons - self._export_csv = QPushButton() - self._export_json = QPushButton() - self._export_xlsx = QPushButton() - self._export_csv.clicked.connect(self._on_export_csv) - self._export_json.clicked.connect(self._on_export_json) - self._export_xlsx.clicked.connect(self._on_export_xlsx) - self._export_csv.setVisible(False) - self._export_json.setVisible(False) - self._export_xlsx.setVisible(False) - - # Footer stats - self._total_label = QLabel("") - self._total_label.setVisible(False) - self._stats_label = QLabel("") - self._stats_label.setObjectName("statsLabel") - self._stats_label.setVisible(False) - - btn_row = QHBoxLayout() - btn_row.addWidget(self._quality_filter) - btn_row.addWidget(self._export_csv) - btn_row.addWidget(self._export_json) - btn_row.addWidget(self._export_xlsx) - btn_row.addStretch() - btn_row.addWidget(self._stats_label) - btn_row.addWidget(self._total_label) - - layout = QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.addWidget(self._search) - layout.addWidget(self._stack) - layout.addLayout(btn_row) - - self.retranslate_ui() - i18n.language_changed.connect(self.retranslate_ui) - - def retranslate_ui(self) -> None: - self._search.setPlaceholderText(tr("search_placeholder")) - self._export_csv.setText(tr("export_csv_btn")) - self._export_json.setText(tr("export_json_btn")) - self._export_xlsx.setText(tr("export_xlsx_btn")) - self._quality_filter.setItemText(0, tr("filter_all")) - self._empty.set_texts( - title=tr("empty_results_title"), - subtitle=tr("empty_results_subtitle"), - ) - - def add_row(self, result: Result) -> None: - self._results.append(result) - self._stack.setCurrentIndex(1) # show table - - if not self._table.columnCount() or (result.data and self._columns == ["item", "status"]): - self._columns = get_columns(self._results) - self._table.setColumnCount(len(self._columns)) - self._table.setHorizontalHeaderLabels(self._columns) - - row = self._table.rowCount() - self._table.insertRow(row) - row_data = {"item": result.item, "status": result.status.value} - row_data.update(result.data) - - for col_idx, col in enumerate(self._columns): - cell_text = str(row_data.get(col, "")) - item = QTableWidgetItem(cell_text) - item.setTextAlignment(Qt.AlignCenter) - - # Color status column - if col == "status": - status_val = result.status.value - fg = _STATUS_COLORS.get(status_val) - bg = _STATUS_BG.get(status_val) - if fg: - item.setForeground(fg) - if bg: - item.setBackground(bg) - - self._table.setItem(row, col_idx, item) - - if any(r.data.get("quality") for r in self._results): - self._quality_filter.setVisible(True) - self._export_csv.setVisible(True) - self._export_json.setVisible(True) - self._export_xlsx.setVisible(True) - self._update_total() - self._update_stats() - - def _update_total(self) -> None: - total = sum(r.data.get("total_usd", 0) for r in self._results if r.data) - if total > 0: - self._total_label.setText(tr("total_fmt").format(total=total)) - self._total_label.setVisible(True) - else: - self._total_label.setVisible(False) - - def _update_stats(self) -> None: - ok = sum(1 for r in self._results if r.status == ResultStatus.OK) - err = sum(1 for r in self._results if r.status == ResultStatus.ERROR) - skip = sum(1 for r in self._results if r.status == ResultStatus.SKIP) - parts = [f"✓ {ok}"] - if err: - parts.append(f"✗ {err}") - if skip: - parts.append(f"⊘ {skip}") - self._stats_label.setText(" │ ".join(parts)) - self._stats_label.setVisible(True) - - def snapshot(self) -> list[Result]: - return list(self._results) - - def restore(self, results: list[Result]) -> None: - self.clear_results() - for r in results: - self.add_row(r) - - def clear_results(self) -> None: - self._results.clear() - self._table.setRowCount(0) - self._table.setColumnCount(0) - self._columns = ["item", "status"] - self._quality_filter.setCurrentIndex(0) - self._quality_filter.setVisible(False) - self._export_csv.setVisible(False) - self._export_json.setVisible(False) - self._export_xlsx.setVisible(False) - self._total_label.setVisible(False) - self._stats_label.setVisible(False) - self._stack.setCurrentIndex(0) # show empty state - - def _apply_filter(self, text: str) -> None: - text = text.lower() - for row in range(self._table.rowCount()): - match = any( - text in (self._table.item(row, col).text().lower() if self._table.item(row, col) else "") - for col in range(self._table.columnCount()) - ) - self._table.setRowHidden(row, not match) - - def _filtered_results(self) -> list[Result]: - quality = self._quality_filter.currentData() - if quality == "all": - return self._results - return [r for r in self._results if r.data.get("quality") == quality] - - def _on_export_csv(self) -> None: - from PySide6.QtWidgets import QFileDialog - path, _ = QFileDialog.getSaveFileName(self, tr("save_csv"), "results.csv", "CSV (*.csv)") - if path: - CsvExporter().export(self._filtered_results(), path) - - def _on_export_json(self) -> None: - from PySide6.QtWidgets import QFileDialog - path, _ = QFileDialog.getSaveFileName(self, tr("save_json"), "results.json", "JSON (*.json)") - if path: - JsonExporter().export(self._filtered_results(), path) - - def _on_export_xlsx(self) -> None: - from PySide6.QtWidgets import QFileDialog - path, _ = QFileDialog.getSaveFileName(self, tr("save_xlsx"), "results.xlsx", "Excel (*.xlsx)") - if path: - XlsxExporter().export(self._filtered_results(), path) diff --git a/app/ui/segmented_tabs.py b/app/ui/segmented_tabs.py deleted file mode 100644 index 8d320d6..0000000 --- a/app/ui/segmented_tabs.py +++ /dev/null @@ -1,56 +0,0 @@ -from __future__ import annotations - -from PySide6.QtCore import Signal -from PySide6.QtWidgets import QButtonGroup, QHBoxLayout, QToolButton, QWidget - -from app.core.base_module import BaseModule - - -class SegmentedModuleTabs(QWidget): - module_selected = Signal(object) # BaseModule - - def __init__(self, modules: list[BaseModule], parent: QWidget | None = None) -> None: - super().__init__(parent) - self._modules = list(modules) - self._buttons: list[QToolButton] = [] - - layout = QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(0) - - self._group = QButtonGroup(self) - self._group.setExclusive(True) - self._group.idClicked.connect(self._on_id_clicked) - - self.setProperty("segmented", True) - - for idx, module in enumerate(self._modules): - btn = QToolButton(self) - btn.setText(module.name) - btn.setCheckable(True) - btn.setAutoExclusive(True) - btn.setProperty("segmentedItem", True) - self._group.addButton(btn, idx) - self._buttons.append(btn) - layout.addWidget(btn) - - if self._buttons: - self._buttons[0].setChecked(True) - - def set_current_index(self, idx: int) -> None: - if idx < 0 or idx >= len(self._buttons): - return - self._buttons[idx].setChecked(True) - self.module_selected.emit(self._modules[idx]) - - def current_module(self) -> BaseModule | None: - checked_id = self._group.checkedId() - if checked_id < 0: - return None - if checked_id >= len(self._modules): - return None - return self._modules[checked_id] - - def _on_id_clicked(self, idx: int) -> None: - if 0 <= idx < len(self._modules): - self.module_selected.emit(self._modules[idx]) diff --git a/app/ui/sidebar.py b/app/ui/sidebar.py deleted file mode 100644 index ad456df..0000000 --- a/app/ui/sidebar.py +++ /dev/null @@ -1,166 +0,0 @@ -from __future__ import annotations - -from pathlib import Path - -from PySide6.QtCore import ( - Qt, Signal, QSize, QPropertyAnimation, QEasingCurve, Property, -) -from PySide6.QtGui import QIcon, QColor, QPainter -from PySide6.QtWidgets import ( - QWidget, QVBoxLayout, QPushButton, QLabel, QSizePolicy, -) - -from app.core.base_module import BaseModule -from app.i18n import tr, i18n - -_ICONS_DIR = Path(__file__).parent / "theme" / "icons" - -# Map module name → icon filename (without extension) -_MODULE_ICON: dict[str, str] = { - "EVM Balance": "evm", - "SVM Balance": "svm", - "Collector": "collector", - "Proxy Check": "proxy", - "Twitter Checker": "twitter", - "Discord Checker": "discord", -} - - -class _SidebarButton(QPushButton): - """One sidebar button: icon + text + status indicator.""" - - def __init__(self, module: BaseModule, parent: QWidget | None = None) -> None: - super().__init__(parent) - self.module = module - self.setCheckable(True) - self.setObjectName("sidebarButton") - self.setCursor(Qt.PointingHandCursor) - - icon_file = _ICONS_DIR / f"{_MODULE_ICON.get(module.name, 'evm')}.svg" - if icon_file.exists(): - self.setIcon(QIcon(str(icon_file))) - self.setIconSize(QSize(22, 22)) - - self.setText(module.name) - self._status: str = "" # "", "running", "done", "error" - - def set_status(self, status: str) -> None: - self._status = status - self.setProperty("runStatus", status) - self.style().unpolish(self) - self.style().polish(self) - self.update() - - def paintEvent(self, event): - super().paintEvent(event) - if not self._status: - return - painter = QPainter(self) - painter.setRenderHint(QPainter.Antialiasing) - colors = { - "running": QColor("#F0C040"), - "done": QColor("#30D158"), - "error": QColor("#FF453A"), - } - color = colors.get(self._status) - if color: - painter.setBrush(color) - painter.setPen(Qt.NoPen) - x = self.width() - 14 - y = 8 - painter.drawEllipse(x, y, 8, 8) - painter.end() - - -class Sidebar(QWidget): - """Vertical sidebar with module icons, collapse animation, and status badges.""" - - module_selected = Signal(object) # BaseModule - EXPANDED_WIDTH = 180 - COLLAPSED_WIDTH = 56 - - def __init__(self, modules: list[BaseModule], parent: QWidget | None = None) -> None: - super().__init__(parent) - self.setObjectName("sidebar") - self._modules = modules - self._buttons: list[_SidebarButton] = [] - self._expanded = True - - self.setFixedWidth(self.EXPANDED_WIDTH) - self.setMinimumHeight(200) - - layout = QVBoxLayout(self) - layout.setContentsMargins(8, 12, 8, 12) - layout.setSpacing(4) - - # Logo / title - self._title = QLabel("FarmerPro") - self._title.setObjectName("sidebarTitle") - self._title.setAlignment(Qt.AlignCenter) - layout.addWidget(self._title) - layout.addSpacing(12) - - # Module buttons - for module in modules: - btn = _SidebarButton(module, self) - btn.clicked.connect(lambda checked, m=module, b=btn: self._on_clicked(m, b)) - self._buttons.append(btn) - layout.addWidget(btn) - - layout.addStretch() - - # Collapse toggle - self._toggle_btn = QPushButton("◀") - self._toggle_btn.setObjectName("sidebarToggle") - self._toggle_btn.setFixedSize(32, 32) - self._toggle_btn.setCursor(Qt.PointingHandCursor) - self._toggle_btn.clicked.connect(self._toggle_collapse) - layout.addWidget(self._toggle_btn, alignment=Qt.AlignCenter) - - # Select first module - if self._buttons: - self._buttons[0].setChecked(True) - - # Animation - self._anim = QPropertyAnimation(self, b"fixedWidth") - self._anim.setDuration(200) - self._anim.setEasingCurve(QEasingCurve.InOutCubic) - - def _on_clicked(self, module: BaseModule, btn: _SidebarButton) -> None: - for b in self._buttons: - if b is not btn: - b.setChecked(False) - btn.setChecked(True) - self.module_selected.emit(module) - - def _toggle_collapse(self) -> None: - self._expanded = not self._expanded - target = self.EXPANDED_WIDTH if self._expanded else self.COLLAPSED_WIDTH - self._anim.setStartValue(self.width()) - self._anim.setEndValue(target) - self._anim.start() - self._toggle_btn.setText("◀" if self._expanded else "▶") - for btn in self._buttons: - btn.setText(btn.module.name if self._expanded else "") - self._title.setVisible(self._expanded) - - def set_module_status(self, module: BaseModule, status: str) -> None: - """Set module status: 'running', 'done', 'error', '' (clear).""" - for btn in self._buttons: - if btn.module is module: - btn.set_status(status) - break - - def select_module(self, index: int) -> None: - """Programmatically select a module by index.""" - if 0 <= index < len(self._buttons): - self._buttons[index].click() - - # Animated fixedWidth property for QPropertyAnimation - def _get_fixed_width(self) -> int: - return self.width() - - def _set_fixed_width(self, w: int) -> None: - self.setFixedWidth(w) - - fixedWidth = Property(int, _get_fixed_width, _set_fixed_width) diff --git a/app/ui/theme.py b/app/ui/theme.py deleted file mode 100644 index 5a7c5ea..0000000 --- a/app/ui/theme.py +++ /dev/null @@ -1,26 +0,0 @@ -from __future__ import annotations - -from pathlib import Path - - -def _theme_path() -> Path: - return Path(__file__).resolve().parent / "theme" / "apple_dark.qss" - - -def _theme_dir_qss_path() -> str: - # QSS стабильнее работает с абсолютными путями вида C:/... чем с file:///... - return _theme_path().parent.resolve().as_posix() - - -def load_apple_dark_qss() -> str: - qss = _theme_path().read_text(encoding="utf-8") - return qss.replace("__THEME_DIR__", _theme_dir_qss_path()) - - -def apply_apple_dark_theme(app) -> None: - """ - Applies the global Apple-like dark theme to the QApplication. - - `app` is intentionally untyped here to avoid importing Qt in non-Qt contexts. - """ - app.setStyleSheet(load_apple_dark_qss()) diff --git a/app/ui/theme/apple_dark.qss b/app/ui/theme/apple_dark.qss deleted file mode 100644 index 155c194..0000000 --- a/app/ui/theme/apple_dark.qss +++ /dev/null @@ -1,580 +0,0 @@ -/* Apple-like dark theme (QSS) — single source of truth */ - -/* Base */ -QMainWindow, -QWidget { - background: #0B0B0F; - color: rgba(245,245,247,0.92); - font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro Display", - "Segoe UI Variable", "Segoe UI", sans-serif; - font-size: 10pt; -} - -/* Containers by objectName */ -#topToolbar { - background: #121217; - border-bottom: 1px solid rgba(255,255,255,0.07); -} - -#appTitle { - font-weight: 650; - font-size: 10pt; - letter-spacing: 0.02em; -} - -#resultsPane { - background: #121217; -} - -#inspectorPane { - background: rgba(255,255,255,0.02); - border-left: 1px solid rgba(255,255,255,0.07); -} - -#logPane { - background: rgba(255,255,255,0.02); - border-top: 1px solid rgba(255,255,255,0.07); -} - -/* ═══ Buttons — polished ═══ */ -QPushButton { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, - stop:0 rgba(255,255,255,0.08), stop:1 rgba(255,255,255,0.04)); - border: 1px solid rgba(255,255,255,0.10); - border-radius: 10px; - padding: 7px 14px; - font-weight: 600; -} -QPushButton:hover { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, - stop:0 rgba(255,255,255,0.12), stop:1 rgba(255,255,255,0.07)); - border-color: rgba(255,255,255,0.14); -} -QPushButton:pressed { - background: rgba(255,255,255,0.14); -} -QPushButton:disabled { - background: rgba(255,255,255,0.03); - color: rgba(245,245,247,0.30); - border-color: rgba(255,255,255,0.05); -} - -QPushButton[primary="true"] { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, - stop:0 #1A8FFF, stop:1 #0A74E8); - border-color: rgba(10,132,255,0.40); - color: #FFFFFF; -} -QPushButton[primary="true"]:hover { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, - stop:0 #409CFF, stop:1 #0A84FF); -} -QPushButton[primary="true"]:pressed { - background: #0068D6; -} - -/* Language toggle (EN / RU) */ -QPushButton[langToggle="true"] { - background: rgba(255,255,255,0.04); - border: 1px solid rgba(255,255,255,0.08); - border-radius: 8px; - padding: 4px 6px; - font-size: 9pt; - font-weight: 600; - color: rgba(245,245,247,0.50); -} -QPushButton[langToggle="true"]:hover { - background: rgba(255,255,255,0.08); - color: rgba(245,245,247,0.75); -} -QPushButton[langToggle="true"]:checked { - background: rgba(10,132,255,0.20); - border-color: rgba(10,132,255,0.45); - color: #0A84FF; -} - -/* Segmented tabs (QToolButton) */ -QWidget[segmented="true"] { - background: rgba(255,255,255,0.06); - border: 1px solid rgba(255,255,255,0.10); - border-radius: 12px; - padding: 3px; -} - -QToolButton[segmentedItem="true"] { - background: transparent; - border: none; - border-radius: 10px; - padding: 7px 10px; - color: rgba(245,245,247,0.72); - font-weight: 650; -} -QToolButton[segmentedItem="true"]:hover { - background: rgba(255,255,255,0.06); -} -QToolButton[segmentedItem="true"]:checked { - background: rgba(255,255,255,0.10); - color: rgba(245,245,247,0.92); -} - -/* Inputs */ -QLineEdit, -QComboBox, -QSpinBox, -QDoubleSpinBox { - background: rgba(255,255,255,0.06); - border: 1px solid rgba(255,255,255,0.10); - border-radius: 10px; - padding: 6px 24px 6px 10px; - selection-background-color: rgba(10,132,255,0.25); -} - -QSpinBox::up-button, -QDoubleSpinBox::up-button { - subcontrol-origin: border; - subcontrol-position: top right; - width: 16px; - height: 11px; - background: rgba(255,255,255,0.03); - border: none; - border-left: 1px solid rgba(255,255,255,0.09); - border-top-right-radius: 9px; - margin: 1px 1px 0 0; -} - -QSpinBox::down-button, -QDoubleSpinBox::down-button { - subcontrol-origin: border; - subcontrol-position: bottom right; - width: 16px; - height: 11px; - background: rgba(255,255,255,0.03); - border: none; - border-left: 1px solid rgba(255,255,255,0.09); - border-bottom-right-radius: 9px; - margin: 0 1px 1px 0; -} - -QSpinBox::up-button:hover, -QSpinBox::down-button:hover, -QDoubleSpinBox::up-button:hover, -QDoubleSpinBox::down-button:hover { - background: rgba(255,255,255,0.065); -} - -QSpinBox::up-button:pressed, -QSpinBox::down-button:pressed, -QDoubleSpinBox::up-button:pressed, -QDoubleSpinBox::down-button:pressed { - background: rgba(255,255,255,0.11); -} - -QSpinBox::up-arrow, -QDoubleSpinBox::up-arrow { - image: url(__THEME_DIR__/up-arrow.svg); - width: 7px; - height: 5px; -} - -QSpinBox::down-arrow, -QDoubleSpinBox::down-arrow { - image: url(__THEME_DIR__/down-arrow.svg); - width: 7px; - height: 5px; -} -QLineEdit:focus, -QComboBox:focus, -QSpinBox:focus, -QDoubleSpinBox:focus, -QPlainTextEdit:focus { - border: 2px solid rgba(10,132,255,0.60); -} - -/* ═══ GroupBox — glass card ═══ */ -QGroupBox { - background: rgba(255,255,255,0.025); - border: 1px solid rgba(255,255,255,0.08); - border-radius: 12px; - margin-top: 14px; - padding: 12px 10px 8px 10px; -} -QGroupBox::title { - subcontrol-origin: margin; - subcontrol-position: top left; - left: 12px; - padding: 0 6px; - color: rgba(245,245,247,0.55); - font-size: 9pt; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.04em; -} - -/* CheckBox */ -QCheckBox { - spacing: 6px; -} -QCheckBox::indicator { - width: 16px; - height: 16px; - border: 1px solid rgba(255,255,255,0.22); - border-radius: 4px; - background: rgba(255,255,255,0.06); -} -QCheckBox::indicator:checked { - background: #0A84FF; - border-color: #0A84FF; -} -QCheckBox::indicator:disabled { - background: rgba(255,255,255,0.03); - border-color: rgba(255,255,255,0.06); -} - -/* Chain picker button — looks like an input field */ -QPushButton#chainPickerButton { - background: rgba(255,255,255,0.06); - border: 1px solid rgba(255,255,255,0.10); - border-radius: 10px; - padding: 6px 28px 6px 10px; - font-weight: 400; - color: rgba(245,245,247,0.92); - text-align: left; -} -QPushButton#chainPickerButton:hover { - background: rgba(255,255,255,0.09); - border-color: rgba(255,255,255,0.14); -} - -/* Chain picker popup */ -QFrame#chainPickerPopup { - background: #1C1F28; - border: 1px solid rgba(255,255,255,0.12); - border-radius: 12px; -} -QFrame#chainPickerPopup QLineEdit { - background: rgba(255,255,255,0.07); - border: 1px solid rgba(255,255,255,0.10); - border-radius: 8px; - padding: 5px 10px; -} -QFrame#chainPickerPopup QListWidget { - background: transparent; - border: none; - outline: none; -} -QFrame#chainPickerPopup QListWidget::item { - padding: 4px 6px; - border-radius: 6px; - color: rgba(245,245,247,0.85); -} -QFrame#chainPickerPopup QListWidget::item:hover { - background: rgba(255,255,255,0.06); -} -QFrame#chainPickerPopup QListWidget::item:selected { - background: transparent; -} - -QComboBox QAbstractItemView { - background: #171A22; - color: rgba(245,245,247,0.92); - border: 1px solid rgba(255,255,255,0.10); - selection-background-color: rgba(10,132,255,0.22); -} - -/* ═══ Splitter — visible handles ═══ */ -QSplitter::handle { - background: rgba(255,255,255,0.04); - margin: 0; -} -QSplitter::handle:horizontal { - width: 3px; -} -QSplitter::handle:vertical { - height: 3px; -} -QSplitter::handle:hover { - background: rgba(10,132,255,0.30); -} -QSplitter::handle:pressed { - background: rgba(10,132,255,0.50); -} - -/* Table */ -QTableWidget { - background: transparent; - gridline-color: rgba(255,255,255,0.06); - border: 1px solid rgba(255,255,255,0.07); - border-radius: 12px; -} -QHeaderView::section { - background: rgba(255,255,255,0.03); - color: rgba(245,245,247,0.72); - padding: 8px 10px; - border: none; - border-bottom: 1px solid rgba(255,255,255,0.07); - font-weight: 700; -} -QTableWidget::item { - padding: 6px 8px; -} -QTableWidget::item:selected { - background: rgba(10,132,255,0.20); -} - -/* Log */ -QPlainTextEdit { - background: transparent; - border: none; - font-family: ui-monospace, "SF Mono", Menlo, Consolas, "Cascadia Mono", monospace; - font-size: 9pt; - color: rgba(245,245,247,0.78); -} - -/* ═══ Toast notifications ═══ */ -#toast { - background: rgba(30,30,38,0.92); - border: 1px solid rgba(255,255,255,0.10); - border-radius: 12px; - padding: 10px 20px; - font-weight: 600; - font-size: 10pt; - color: rgba(245,245,247,0.90); -} -#toast[level="success"] { - background: rgba(48,209,88,0.18); - border-color: rgba(48,209,88,0.35); - color: #30D158; -} -#toast[level="error"] { - background: rgba(255,69,58,0.18); - border-color: rgba(255,69,58,0.35); - color: #FF453A; -} -#toast[level="warning"] { - background: rgba(255,214,10,0.14); - border-color: rgba(255,214,10,0.30); - color: #FFD60A; -} - -/* ═══ Empty State ═══ */ -#emptyState { - background: transparent; -} -#emptyStateIcon { - font-size: 36pt; - color: rgba(245,245,247,0.25); -} -#emptyStateTitle { - font-size: 12pt; - font-weight: 600; - color: rgba(245,245,247,0.55); -} -#emptyStateSubtitle { - font-size: 9pt; - color: rgba(245,245,247,0.35); - max-width: 300px; -} - -/* ═══ Drop Zone ═══ */ -#dropArea { - background: rgba(255,255,255,0.02); - border: 2px dashed rgba(255,255,255,0.12); - border-radius: 10px; - margin: 4px 0; -} -#dropArea:hover { - border-color: rgba(10,132,255,0.40); - background: rgba(10,132,255,0.04); -} -#dropZone[dragOver="true"] #dropArea { - border-color: rgba(10,132,255,0.60); - background: rgba(10,132,255,0.08); -} -#dropHint { - color: rgba(245,245,247,0.35); - font-size: 9pt; -} -#dropZoneStatus { - font-size: 9pt; - color: rgba(245,245,247,0.55); -} - -/* ═══ Sidebar ═══ */ -#sidebar { - background: #0F0F14; - border-right: 1px solid rgba(255,255,255,0.07); -} - -#sidebarTitle { - font-weight: 700; - font-size: 13pt; - color: rgba(245,245,247,0.92); - padding: 4px 0; - letter-spacing: 0.03em; -} - -#sidebarButton { - background: transparent; - border: none; - border-radius: 10px; - padding: 10px 12px; - text-align: left; - font-weight: 500; - font-size: 10pt; - color: rgba(245,245,247,0.55); -} -#sidebarButton:hover { - background: rgba(255,255,255,0.06); - color: rgba(245,245,247,0.80); -} -#sidebarButton:checked { - background: rgba(10,132,255,0.15); - color: #0A84FF; -} - -#sidebarToggle { - background: rgba(255,255,255,0.04); - border: 1px solid rgba(255,255,255,0.08); - border-radius: 8px; - color: rgba(245,245,247,0.40); - font-size: 9pt; -} -#sidebarToggle:hover { - background: rgba(255,255,255,0.08); - color: rgba(245,245,247,0.70); -} - -/* ═══ Results Table ═══ */ -#resultsSearch { - border-radius: 10px; - padding: 8px 12px; - margin-bottom: 4px; -} - -QTableWidget { - background: transparent; - gridline-color: transparent; - border: 1px solid rgba(255,255,255,0.07); - border-radius: 12px; - alternate-background-color: rgba(255,255,255,0.02); -} -QHeaderView::section { - background: rgba(255,255,255,0.03); - color: rgba(245,245,247,0.72); - padding: 8px 10px; - border: none; - border-bottom: 1px solid rgba(255,255,255,0.07); - font-weight: 700; - font-size: 9pt; - text-transform: uppercase; - letter-spacing: 0.04em; -} -QTableWidget::item { - padding: 8px 10px; - border-bottom: 1px solid rgba(255,255,255,0.03); -} -QTableWidget::item:selected { - background: rgba(10,132,255,0.15); -} - -#statsLabel { - font-size: 9pt; - color: rgba(245,245,247,0.50); - font-weight: 600; -} - -/* ═══ Log Widget ═══ */ -QPushButton[logFilter="true"] { - background: rgba(255,255,255,0.04); - border: 1px solid rgba(255,255,255,0.08); - border-radius: 6px; - padding: 2px 10px; - font-size: 8pt; - font-weight: 600; - color: rgba(245,245,247,0.45); -} -QPushButton[logFilter="true"]:hover { - background: rgba(255,255,255,0.07); - color: rgba(245,245,247,0.70); -} -QPushButton[logFilter="true"]:checked { - background: rgba(10,132,255,0.18); - border-color: rgba(10,132,255,0.35); - color: #0A84FF; -} - -#logSearch { - border-radius: 6px; - padding: 3px 8px; - font-size: 8pt; -} - -#logText { - background: transparent; - border: none; - font-family: ui-monospace, "SF Mono", Menlo, Consolas, "Cascadia Mono", monospace; - font-size: 9pt; - color: rgba(245,245,247,0.78); -} - -/* ═══ Scrollbar — minimal ═══ */ -QScrollBar:vertical { - background: transparent; - width: 8px; - margin: 0; -} -QScrollBar::handle:vertical { - background: rgba(255,255,255,0.10); - border-radius: 4px; - min-height: 30px; -} -QScrollBar::handle:vertical:hover { - background: rgba(255,255,255,0.18); -} -QScrollBar::add-line, QScrollBar::sub-line, -QScrollBar::add-page, QScrollBar::sub-page { - background: none; - border: none; - height: 0; -} - -QScrollBar:horizontal { - background: transparent; - height: 8px; - margin: 0; -} -QScrollBar::handle:horizontal { - background: rgba(255,255,255,0.10); - border-radius: 4px; - min-width: 30px; -} -QScrollBar::handle:horizontal:hover { - background: rgba(255,255,255,0.18); -} - -/* ═══ Tooltips ═══ */ -QToolTip { - background: #1C1F28; - color: rgba(245,245,247,0.88); - border: 1px solid rgba(255,255,255,0.12); - border-radius: 8px; - padding: 6px 10px; - font-size: 9pt; -} - -/* ═══ Progress ═══ */ -#mainProgress { - background: rgba(255,255,255,0.04); - border: none; - border-radius: 2px; -} -#mainProgress::chunk { - background: qlineargradient(x1:0, y1:0, x2:1, y2:0, - stop:0 #0A84FF, stop:1 #5AC8FA); - border-radius: 2px; -} -#progressLabel { - font-size: 9pt; - color: rgba(245,245,247,0.50); - font-weight: 600; -} diff --git a/app/ui/theme/down-arrow.svg b/app/ui/theme/down-arrow.svg deleted file mode 100644 index 51ee52a..0000000 --- a/app/ui/theme/down-arrow.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/ui/theme/icons/collector.svg b/app/ui/theme/icons/collector.svg deleted file mode 100644 index 99b6da6..0000000 --- a/app/ui/theme/icons/collector.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/ui/theme/icons/discord.svg b/app/ui/theme/icons/discord.svg deleted file mode 100644 index 4e1a2c6..0000000 --- a/app/ui/theme/icons/discord.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/app/ui/theme/icons/drop.svg b/app/ui/theme/icons/drop.svg deleted file mode 100644 index 7f95930..0000000 --- a/app/ui/theme/icons/drop.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/app/ui/theme/icons/evm.svg b/app/ui/theme/icons/evm.svg deleted file mode 100644 index 5b6819b..0000000 --- a/app/ui/theme/icons/evm.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/app/ui/theme/icons/proxy.svg b/app/ui/theme/icons/proxy.svg deleted file mode 100644 index 85d7366..0000000 --- a/app/ui/theme/icons/proxy.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/app/ui/theme/icons/svm.svg b/app/ui/theme/icons/svm.svg deleted file mode 100644 index a80f5b5..0000000 --- a/app/ui/theme/icons/svm.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/app/ui/theme/icons/twitter.svg b/app/ui/theme/icons/twitter.svg deleted file mode 100644 index 78e3520..0000000 --- a/app/ui/theme/icons/twitter.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/ui/theme/spinbox-chevron-down-disabled.svg b/app/ui/theme/spinbox-chevron-down-disabled.svg deleted file mode 100644 index 8ded02d..0000000 --- a/app/ui/theme/spinbox-chevron-down-disabled.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/app/ui/theme/spinbox-chevron-down.svg b/app/ui/theme/spinbox-chevron-down.svg deleted file mode 100644 index df4c321..0000000 --- a/app/ui/theme/spinbox-chevron-down.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/app/ui/theme/spinbox-chevron-up-disabled.svg b/app/ui/theme/spinbox-chevron-up-disabled.svg deleted file mode 100644 index bb9ab32..0000000 --- a/app/ui/theme/spinbox-chevron-up-disabled.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/app/ui/theme/spinbox-chevron-up.svg b/app/ui/theme/spinbox-chevron-up.svg deleted file mode 100644 index 3af528b..0000000 --- a/app/ui/theme/spinbox-chevron-up.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/app/ui/theme/up-arrow.svg b/app/ui/theme/up-arrow.svg deleted file mode 100644 index ad431c6..0000000 --- a/app/ui/theme/up-arrow.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/ui/widgets/__init__.py b/app/ui/widgets/__init__.py deleted file mode 100644 index dbf9827..0000000 --- a/app/ui/widgets/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from app.ui.widgets.drop_zone import DropZone -from app.ui.widgets.toast import Toast, show_toast -from app.ui.widgets.empty_state import EmptyState - -__all__ = ["DropZone", "Toast", "show_toast", "EmptyState"] diff --git a/app/ui/widgets/chain_picker.py b/app/ui/widgets/chain_picker.py deleted file mode 100644 index 1d492a8..0000000 --- a/app/ui/widgets/chain_picker.py +++ /dev/null @@ -1,251 +0,0 @@ -# app/ui/widgets/chain_picker.py -from __future__ import annotations - -from PySide6.QtCore import Qt, Signal, QPoint -from PySide6.QtWidgets import ( - QWidget, QVBoxLayout, QHBoxLayout, QFrame, - QLineEdit, QListWidget, QListWidgetItem, QPushButton, - QSizePolicy, -) - -from app.integrations.lifi_client import DEBANK_TO_CHAIN_ID -from app.i18n import tr, i18n - -# DeBank key → human readable display name -_DISPLAY: dict[str, str] = { - "eth": "Ethereum", - "bsc": "BNB Chain", - "matic": "Polygon", - "avax": "Avalanche", - "op": "Optimism", - "arb": "Arbitrum", - "base": "Base", - "era": "zkSync Era", - "linea": "Linea", - "scrl": "Scroll", - "blast": "Blast", - "mnt": "Mantle", - "mode": "Mode", - "frax": "Fraxtal", - "opbnb": "opBNB", - "taiko": "Taiko", - "abs": "Abstract", - "ink": "Ink", - "soneium": "Soneium", - "uni": "Unichain", - "world": "World Chain", - "morph": "Morph", - "swell": "Swellchain", - "bob": "BOB", - "sophon": "Sophon", - "xdai": "Gnosis", - "celo": "Celo", - "cro": "Cronos", - "boba": "Boba", - "metis": "Metis", - "mobm": "Moonbeam", - "fuse": "Fuse", - "klay": "Klaytn", - "rsk": "RSK", - "tlos": "Telos", - "flr": "Flare", - "ron": "Ronin", - "sei": "Sei", - "gravity": "Gravity", - "lisk": "Lisk", - "ape": "ApeChain", - "ethlink": "Etherlink", - "sonic": "Sonic", - "corn": "Corn", - "bera": "Berachain", - "lens": "Lens", - "hyper": "HyperEVM", - "hemi": "Hemi", - "plume": "Plume", - "katana": "Katana", - "plasma": "Plasma", - "monad": "Monad", - "stable": "Stable", - "megaeth": "MegaETH", - "itze": "Ithaca", - "ftm": "Fantom", - "movr": "Moonriver", - "iotx": "IoTeX", - "dfk": "DFK Chain", - "nova": "Arbitrum Nova", - "doge": "Dogechain", - "kava": "Kava", - "cfx": "Conflux", - "core": "Core", - "wemix": "WEMIX", - "oas": "Oasys", - "zora": "Zora", - "manta": "Manta", - "zeta": "ZetaChain", - "merlin": "Merlin", - "xlayer": "X Layer", - "btr": "Bitlayer", - "b2": "B2 Network", - "croze": "Cronos zkEVM", - "zircuit": "Zircuit", - "hsk": "HashKey", - "story": "Story", - "cyber": "Cyber", - "chiliz": "Chiliz", - "orderly": "Orderly", - "rari": "RARI Chain", - "reya": "Reya", - "bb": "BounceBit", - "goat": "GOAT", - "tac": "TAC", - "botanix": "Botanix", -} - - -def _display_name(key: str) -> str: - return _DISPLAY.get(key, key.capitalize()) - - -class _ChainPickerPopup(QFrame): - """Floating popup with search field and checkbox list.""" - - def __init__(self, picker: "ChainPickerWidget") -> None: - super().__init__(None, Qt.Popup | Qt.FramelessWindowHint) - self._picker = picker - self.setObjectName("chainPickerPopup") - self.setFixedWidth(260) - - layout = QVBoxLayout(self) - layout.setContentsMargins(6, 6, 6, 6) - layout.setSpacing(4) - - self._search = QLineEdit() - self._search.setPlaceholderText(tr("chain_search_placeholder")) - self._search.textChanged.connect(self._filter) - layout.addWidget(self._search) - - self._list = QListWidget() - self._list.setFixedHeight(240) - self._list.itemChanged.connect(self._on_item_changed) - layout.addWidget(self._list) - - self._populate() - - i18n.language_changed.connect(self.retranslate_ui) - - def retranslate_ui(self) -> None: - self._search.setPlaceholderText(tr("chain_search_placeholder")) - - def _populate(self) -> None: - self._list.blockSignals(True) - self._list.clear() - for key, name in self._picker._all_chains: - item = QListWidgetItem(f"{name} ({key})") - item.setData(Qt.UserRole, key) - item.setFlags(item.flags() | Qt.ItemIsUserCheckable) - item.setCheckState( - Qt.Checked if key in self._picker._selected else Qt.Unchecked - ) - self._list.addItem(item) - self._list.blockSignals(False) - - def refresh_checks(self) -> None: - self._list.blockSignals(True) - for i in range(self._list.count()): - item = self._list.item(i) - key = item.data(Qt.UserRole) - item.setCheckState( - Qt.Checked if key in self._picker._selected else Qt.Unchecked - ) - self._list.blockSignals(False) - - def _filter(self, text: str) -> None: - query = text.strip().lower() - for i in range(self._list.count()): - item = self._list.item(i) - key = item.data(Qt.UserRole) - name = _display_name(key).lower() - item.setHidden(bool(query) and query not in key and query not in name) - - def _on_item_changed(self, item: QListWidgetItem) -> None: - key = item.data(Qt.UserRole) - if item.checkState() == Qt.Checked: - self._picker._selected.add(key) - else: - self._picker._selected.discard(key) - self._picker._update_summary() - - def show_below(self, widget: QWidget) -> None: - pos = widget.mapToGlobal(QPoint(0, widget.height() + 2)) - self.move(pos) - self._search.clear() - self._filter("") - self.show() - self._search.setFocus() - - -class ChainPickerWidget(QWidget): - """Multi-select chain picker that looks like an input field with a dropdown.""" - - selectionChanged = Signal(list) # list[str] of DeBank keys - - def __init__(self, placeholder: str = None, parent=None) -> None: - super().__init__(parent) - self._selected: set[str] = set() - self._placeholder = placeholder or tr("pick_chains_placeholder") - self._all_chains: list[tuple[str, str]] = [ - (key, _display_name(key)) for key in DEBANK_TO_CHAIN_ID - ] - - layout = QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - - self._btn = QPushButton(self._placeholder) - self._btn.setObjectName("chainPickerButton") - self._btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) - self._btn.clicked.connect(self._toggle_popup) - layout.addWidget(self._btn) - - self._popup = _ChainPickerPopup(self) - - i18n.language_changed.connect(self.retranslate_ui) - - def retranslate_ui(self) -> None: - self._placeholder = tr("pick_chains_placeholder") - if not self._selected: - self._update_summary() - - def _toggle_popup(self) -> None: - if self._popup.isVisible(): - self._popup.hide() - else: - self._popup.refresh_checks() - self._popup.show_below(self._btn) - - def _update_summary(self) -> None: - if not self._selected: - self._btn.setText(self._placeholder) - else: - keys = sorted(self._selected) - # Show as many keys as fit (~40 chars), then "+N more" - shown: list[str] = [] - total = 0 - for k in keys: - if total + len(k) + 2 > 38: - break - shown.append(k) - total += len(k) + 2 - remainder = len(keys) - len(shown) - text = ", ".join(shown) - if remainder: - text += f" +{remainder}" - self._btn.setText(text) - self.selectionChanged.emit(list(self._selected)) - - def get_selected(self) -> list[str]: - return sorted(self._selected) - - def set_selected(self, keys: list[str]) -> None: - self._selected = set(keys) - self._update_summary() - self._popup.refresh_checks() diff --git a/app/ui/widgets/drop_zone.py b/app/ui/widgets/drop_zone.py deleted file mode 100644 index 169d623..0000000 --- a/app/ui/widgets/drop_zone.py +++ /dev/null @@ -1,125 +0,0 @@ -from __future__ import annotations - -from pathlib import Path - -from PySide6.QtCore import Qt, Signal -from PySide6.QtGui import QDragEnterEvent, QDropEvent -from PySide6.QtWidgets import ( - QWidget, QVBoxLayout, QHBoxLayout, QLabel, - QPushButton, QLineEdit, QFileDialog, -) - -from app.i18n import tr, i18n - - -class DropZone(QWidget): - """Зона Drag & Drop + кнопка Browse. Заменяет стандартную строку выбора файла.""" - - file_dropped = Signal(str) # Путь к файлу - - def __init__( - self, - label: str = "", - placeholder: str = "file.txt", - file_filter: str = "Text files (*.txt);;All files (*)", - parent: QWidget | None = None, - ) -> None: - super().__init__(parent) - self.setAcceptDrops(True) - self.setObjectName("dropZone") - self._file_filter = file_filter - self._file_path: str = "" - - layout = QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(4) - - # Row: Label + path + browse button - row = QHBoxLayout() - self._label = QLabel(label) - self._label.setObjectName("dropZoneLabel") - row.addWidget(self._label) - - self._path_edit = QLineEdit() - self._path_edit.setPlaceholderText(placeholder) - self._path_edit.setReadOnly(True) - row.addWidget(self._path_edit) - - self._browse_btn = QPushButton(tr("browse_btn")) - self._browse_btn.clicked.connect(self._browse) - row.addWidget(self._browse_btn) - layout.addLayout(row) - - # Drop area (hidden by default, shown when no file loaded) - self._drop_area = QWidget() - self._drop_area.setObjectName("dropArea") - self._drop_area.setFixedHeight(64) - drop_layout = QVBoxLayout(self._drop_area) - drop_layout.setAlignment(Qt.AlignCenter) - self._drop_hint = QLabel(tr("drop_hint")) - self._drop_hint.setObjectName("dropHint") - self._drop_hint.setAlignment(Qt.AlignCenter) - drop_layout.addWidget(self._drop_hint) - layout.addWidget(self._drop_area) - - # Status - self._status = QLabel(tr("file_not_loaded")) - self._status.setObjectName("dropZoneStatus") - layout.addWidget(self._status) - - i18n.language_changed.connect(self._retranslate) - - def _retranslate(self) -> None: - self._browse_btn.setText(tr("browse_btn")) - self._drop_hint.setText(tr("drop_hint")) - if not self._file_path: - self._status.setText(tr("file_not_loaded")) - - def set_label(self, text: str) -> None: - self._label.setText(text) - - def set_status(self, text: str) -> None: - self._status.setText(text) - if text and "✓" in text: - self._drop_area.setVisible(False) - else: - self._drop_area.setVisible(True) - - def get_file_path(self) -> str: - return self._file_path - - def _browse(self) -> None: - path, _ = QFileDialog.getOpenFileName( - self, tr("select_file"), "", self._file_filter, - ) - if path: - self._set_file(path) - - def _set_file(self, path: str) -> None: - self._file_path = path - self._path_edit.setText(path) - self._drop_area.setVisible(False) - self.file_dropped.emit(path) - - # --- Drag & Drop --- - def dragEnterEvent(self, event: QDragEnterEvent) -> None: - if event.mimeData().hasUrls(): - self.setProperty("dragOver", True) - self.style().unpolish(self) - self.style().polish(self) - event.acceptProposedAction() - - def dragLeaveEvent(self, event) -> None: - self.setProperty("dragOver", False) - self.style().unpolish(self) - self.style().polish(self) - - def dropEvent(self, event: QDropEvent) -> None: - self.setProperty("dragOver", False) - self.style().unpolish(self) - self.style().polish(self) - urls = event.mimeData().urls() - if urls: - path = urls[0].toLocalFile() - if path and Path(path).is_file(): - self._set_file(path) diff --git a/app/ui/widgets/empty_state.py b/app/ui/widgets/empty_state.py deleted file mode 100644 index 96d9ae0..0000000 --- a/app/ui/widgets/empty_state.py +++ /dev/null @@ -1,47 +0,0 @@ -from __future__ import annotations - -from PySide6.QtCore import Qt -from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel - - -class EmptyState(QWidget): - """Виджет пустого состояния — иконка + заголовок + подсказка.""" - - def __init__( - self, - icon_text: str = "📋", - title: str = "", - subtitle: str = "", - parent: QWidget | None = None, - ) -> None: - super().__init__(parent) - self.setObjectName("emptyState") - - layout = QVBoxLayout(self) - layout.setAlignment(Qt.AlignCenter) - layout.setSpacing(8) - - self._icon = QLabel(icon_text) - self._icon.setObjectName("emptyStateIcon") - self._icon.setAlignment(Qt.AlignCenter) - layout.addWidget(self._icon) - - self._title = QLabel(title) - self._title.setObjectName("emptyStateTitle") - self._title.setAlignment(Qt.AlignCenter) - self._title.setWordWrap(True) - layout.addWidget(self._title) - - self._subtitle = QLabel(subtitle) - self._subtitle.setObjectName("emptyStateSubtitle") - self._subtitle.setAlignment(Qt.AlignCenter) - self._subtitle.setWordWrap(True) - layout.addWidget(self._subtitle) - - def set_texts(self, icon: str = "", title: str = "", subtitle: str = "") -> None: - if icon: - self._icon.setText(icon) - if title: - self._title.setText(title) - if subtitle: - self._subtitle.setText(subtitle) diff --git a/app/ui/widgets/toast.py b/app/ui/widgets/toast.py deleted file mode 100644 index d883046..0000000 --- a/app/ui/widgets/toast.py +++ /dev/null @@ -1,70 +0,0 @@ -from __future__ import annotations - -from PySide6.QtCore import Qt, QTimer, QPropertyAnimation, QEasingCurve, Property -from PySide6.QtWidgets import QLabel, QWidget, QGraphicsOpacityEffect - - -class Toast(QLabel): - """Всплывающее уведомление, исчезающее через несколько секунд.""" - - def __init__( - self, - text: str, - level: str = "info", - duration_ms: int = 3000, - parent: QWidget | None = None, - ) -> None: - super().__init__(text, parent) - self.setObjectName("toast") - self.setProperty("level", level) - self.setAlignment(Qt.AlignCenter) - self.setWordWrap(True) - self.setFixedHeight(44) - self.setMinimumWidth(280) - self.adjustSize() - - self._opacity = QGraphicsOpacityEffect(self) - self._opacity.setOpacity(0.0) - self.setGraphicsEffect(self._opacity) - - # Fade in - self._fade_in = QPropertyAnimation(self._opacity, b"opacity") - self._fade_in.setDuration(250) - self._fade_in.setStartValue(0.0) - self._fade_in.setEndValue(1.0) - self._fade_in.setEasingCurve(QEasingCurve.OutCubic) - - # Fade out → destroy - self._fade_out = QPropertyAnimation(self._opacity, b"opacity") - self._fade_out.setDuration(400) - self._fade_out.setStartValue(1.0) - self._fade_out.setEndValue(0.0) - self._fade_out.setEasingCurve(QEasingCurve.InCubic) - self._fade_out.finished.connect(self.deleteLater) - - # Auto-dismiss timer - self._timer = QTimer(self) - self._timer.setSingleShot(True) - self._timer.setInterval(duration_ms) - self._timer.timeout.connect(self._fade_out.start) - - def show_toast(self) -> None: - """Показать toast. Вызывать после добавления в layout.""" - self.show() - self._fade_in.start() - self._timer.start() - - -def show_toast( - parent: QWidget, - text: str, - level: str = "info", - duration_ms: int = 3000, -) -> Toast: - """Утилита: создаёт и показывает toast поверх parent.""" - toast = Toast(text, level=level, duration_ms=duration_ms, parent=parent) - # Position at top center - toast.setFixedWidth(min(400, parent.width() - 40)) - toast.move((parent.width() - toast.width()) // 2, 12) - toast.show_toast() - return toast diff --git a/commit_msg.txt b/commit_msg.txt deleted file mode 100644 index 0373e2d..0000000 --- a/commit_msg.txt +++ /dev/null @@ -1,10 +0,0 @@ -Fix: Explicitly set status to ERROR for uncorroborated balances - -Currently, if the attempt budget is exhausted without reaching -CORROBORATION_MIN_AGREE, the fallback logic selects the most conservative -snapshot and returns it but leaves its status as "OK". This causes `main.py` -to accept this potentially phantom balance as a valid result. - -This commit adds `chosen["status"] = "ERROR"` to the fallback logic so that -uncorroborated balances are correctly flagged as errors and excluded from the -successful totals. diff --git a/debank_checker/__init__.py b/debank_checker/__init__.py new file mode 100644 index 0000000..3f14aa0 --- /dev/null +++ b/debank_checker/__init__.py @@ -0,0 +1,5 @@ +""" +DeBank Balance Checker — массовая проверка балансов кошельков через DeBank API. +""" + +__version__ = "1.0.3" diff --git a/debank_checker/api/__init__.py b/debank_checker/api/__init__.py new file mode 100644 index 0000000..d27bfa5 --- /dev/null +++ b/debank_checker/api/__init__.py @@ -0,0 +1,5 @@ +"""API: DeBank клиент.""" + +from debank_checker.api.client import DeBankClient + +__all__ = ["DeBankClient"] diff --git a/debank_checker/api/client.py b/debank_checker/api/client.py new file mode 100644 index 0000000..a725c04 --- /dev/null +++ b/debank_checker/api/client.py @@ -0,0 +1,182 @@ +""" +DeBank API Client +Подпись HMAC-SHA256, curl_cffi (impersonate chrome). +Прокси обязателен. +""" + +import hashlib +import hmac as hmac_lib +import json +import random +import time +import uuid +from typing import Optional + +import curl_cffi.requests as cffi_requests + +from debank_checker.config import ( + API_KEY_INIT, + NFT_POLL_INTERVAL, + NFT_POLL_MAX_WAIT, + NFT_REQUEST_TIMEOUT, + REQUEST_TIMEOUT, +) + +API_BASE = "https://api.debank.com" +NONCE_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz" +NONCE_LENGTH = 40 + + +def sort_params(params: dict) -> str: + if not params: + return "" + return "&".join(f"{k}={v}" for k, v in sorted(params.items())) + + +def sha256_hex(text: str) -> str: + return hashlib.sha256(text.encode("utf-8")).hexdigest() + + +def hmac_sha256(key_str: str, msg_str: str) -> str: + return hmac_lib.new( + key_str.encode("utf-8"), + msg_str.encode("utf-8"), + hashlib.sha256, + ).hexdigest() + + +def generate_nonce() -> str: + return "n_" + "".join(random.choices(NONCE_ALPHABET, k=NONCE_LENGTH)) + + +def sign_request( + params: dict, + method: str, + path: str, + nonce: str | None = None, + ts: int | None = None, + version: str = "v2", +) -> dict: + ts = ts or int(time.time()) + nonce = nonce or generate_nonce() + prefix = "debank-web\n" if version == "v2.1" else "debank-api\n" + sorted_p = sort_params(params) + K = sha256_hex(f"{prefix}{nonce}\n{ts}") + M = sha256_hex(f"{method.upper()}\n{path}\n{sorted_p}") + signature = hmac_sha256(K, M) + return {"signature": signature, "nonce": nonce, "ts": ts, "version": version} + + +class DeBankClient: + """Клиент DeBank API. Прокси обязателен.""" + + def __init__(self, proxy: str, impersonate: str = "chrome124"): + if not proxy: + raise ValueError("Прокси обязателен для DeBank API") + self._api_key = API_KEY_INIT + self._init_ts = int(time.time()) + self._random_at = self._init_ts + self._random_id = uuid.uuid4().hex + self._impersonate = impersonate + proxies = {"https": proxy, "http": proxy} + self._session = cffi_requests.Session( + impersonate=impersonate, + proxies=proxies, + ) + + def _build_headers(self, params: dict, method: str, path: str) -> dict: + sign = sign_request(params, method, path) + account = json.dumps( + { + "random_at": self._random_at, + "random_id": self._random_id, + "user_addr": None, + "connected_addr": None, + }, + separators=(",", ":"), + ) + return { + "Referer": "https://debank.com/", + "Origin": "https://debank.com", + "X-API-Key": self._api_key, + "X-API-Time": str(self._init_ts), + "x-api-ts": str(sign["ts"]), + "x-api-nonce": sign["nonce"], + "x-api-ver": sign["version"], + "x-api-sign": sign["signature"], + "source": "web", + "account": account, + } + + def _get(self, path: str, params: dict | None = None, timeout: float | None = None) -> dict: + params = params or {} + headers = self._build_headers(params, "GET", path) + resp = self._session.get( + API_BASE + path, + params=params, + headers=headers, + timeout=timeout or REQUEST_TIMEOUT, + ) + resp.raise_for_status() + + new_key = resp.headers.get("x-set-api-key") + if new_key: + self._api_key = new_key + + data = resp.json() + if isinstance(data, dict) and "data" in data: + return data["data"] + return data + + def get_total_balance_cache(self, address: str) -> list: + """Кэшированный список токенов по всем сетям.""" + result = self._get("/token/cache_balance_list", {"user_addr": address}) + return result if isinstance(result, list) else [] + + def get_portfolio(self, address: str) -> list: + """DeFi протоколы с позициями.""" + result = self._get("/portfolio/project_list", {"user_addr": address}) + return result if isinstance(result, list) else [] + + def get_nft_used_chains(self, address: str) -> list: + """Сети, в которых у пользователя есть NFT.""" + result = self._get("/nft/used_chains", {"user_addr": address}) + if isinstance(result, list): + return [ + c.get("id") if isinstance(c, dict) else str(c) + for c in result + if isinstance(c, (dict, str)) + ] + return [] + + def get_nft_collection_list(self, address: str, chain: str) -> list: + """ + NFT-коллекции по адресу и сети. + API может вернуть async job (pending) — тогда polling с retry. + Результат: list из result["data"] или result (если list). + """ + params = {"user_addr": address, "chain": chain} + deadline = time.time() + NFT_POLL_MAX_WAIT + + def _extract_list(val) -> list: + if isinstance(val, list): + return val + if isinstance(val, dict) and "data" in val: + d = val["data"] + return d if isinstance(d, list) else [] + return [] + + while True: + result = self._get("/nft/collection_list", params, timeout=NFT_REQUEST_TIMEOUT) + if isinstance(result, list): + return result + if isinstance(result, dict): + job = result.get("job") or {} + status = job.get("status") if job else None + res = result.get("result") + if res is not None: + return _extract_list(res) + if status == "pending" and time.time() < deadline: + time.sleep(NFT_POLL_INTERVAL) + continue + return [] diff --git a/debank_checker/checker.py b/debank_checker/checker.py new file mode 100644 index 0000000..5467d22 --- /dev/null +++ b/debank_checker/checker.py @@ -0,0 +1,363 @@ +""" +Проверка балансов кошельков: tokens + portfolio (+ NFT заглушка) +""" + +import time +from typing import Any + +from debank_checker.api.client import DeBankClient +from debank_checker.config import ( + CORROBORATION_ABS_TOL, + CORROBORATION_ENABLED, + CORROBORATION_MAX_FETCHES, + CORROBORATION_MIN_AGREE, + CORROBORATION_REL_TOL, + DEBUG, + MIN_VALUE_DISPLAY, + RETRY_ATTEMPTS, +) +from debank_checker.proxy.manager import ProxyManager + + +def _mask_proxy(proxy: str | None) -> str: + """Маскирует прокси для лога (оставляет ip:port).""" + if not proxy: + return "—" + try: + if "@" in proxy: + return proxy.split("@")[-1] + return proxy + except Exception: + return "?" + + +def _safe_position_value(item: dict) -> float: + """ + Вычисляет стоимость позиции протокола, защищаясь от фантомных данных API. + + Логика: + - Пересчитывает стоимость из asset_token_list (фильтруя скам-токены). + - Если asset_token_list пустой или все токены скам → возвращает 0. + - Использует min(api_value, recalc_value): + * Для lending: api_value (залог−долг) < recalc_value (только залог) → берём api_value. + * Для farming/common: recalc_value ≈ api_value → результат корректный. + * Для фантомной позиции: recalc_value = 0 → возвращаем 0. + """ + api_value = max(0.0, item.get("stats", {}).get("net_usd_value", 0)) + + asset_tokens = item.get("asset_token_list", []) + if not asset_tokens: + return 0.0 # нет токенов — фантомная позиция + + recalc = 0.0 + for t in asset_tokens: + value = t.get("price", 0) * t.get("amount", 0) + if t.get("is_verified", True) and not t.get("is_scam", False): + recalc += value + + recalc = max(0.0, recalc) + if recalc == 0.0: + return 0.0 # все токены в позиции — скам + + return min(api_value, recalc) + + +def _write_log(address: str, total_usd: float, tokens_usd: float, + protocols_usd: float, tokens: list, portfolio: list) -> None: + """Пишет лог в logs.txt в корневой директории проекта.""" + import json + from pathlib import Path + from datetime import datetime + + log_path = Path(__file__).parent.parent / "logs.txt" + timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + with open(log_path, "a", encoding="utf-8") as f: + f.write(f"\n{'=' * 60}\n") + f.write(f"[{timestamp}] {address}\n") + f.write(f" total_usd={total_usd:.2f} tokens_usd={tokens_usd:.2f}" + f" protocols_usd={protocols_usd:.2f}\n") + if total_usd > 1_000: + f.write(" [!] АНОМАЛЬНЫЙ БАЛАНС — сохраняю сырые данные\n") + f.write(" --- TOKENS ---\n") + f.write(json.dumps(tokens, ensure_ascii=False, indent=2)) + f.write("\n --- PORTFOLIO ---\n") + f.write(json.dumps(portfolio, ensure_ascii=False, indent=2)) + f.write("\n") + + +def _agree(a: float, b: float) -> bool: + """Две суммы считаются согласованными в пределах относ./абс. допуска.""" + return abs(a - b) <= max(CORROBORATION_ABS_TOL, + CORROBORATION_REL_TOL * max(abs(a), abs(b))) + + +def _largest_agreeing_cluster(snaps: list[dict[str, Any]]) -> list[dict[str, Any]]: + """Наибольшая группа выборок, согласованных по total_usd. + + При равенстве размеров выбирается группа с НАИМЕНЬШИМ значением — фантом + всегда завышает баланс, поэтому консервативный выбор защищает от инфляции. + """ + best: list[dict[str, Any]] = [] + for anchor in snaps: + cluster = [s for s in snaps if _agree(s["total_usd"], anchor["total_usd"])] + if (len(cluster) > len(best) + or (len(cluster) == len(best) and best + and _rep(cluster)["total_usd"] < _rep(best)["total_usd"])): + best = cluster + return best + + +def _rep(cluster: list[dict[str, Any]]) -> dict[str, Any]: + """Представитель группы: выборка с минимальным total_usd (все согласованы).""" + return min(cluster, key=lambda s: s["total_usd"]) + + +def _fetch_snapshot(address: str, proxy: str) -> dict[str, Any]: + """Одна независимая выборка баланса кошелька (tokens + portfolio + NFT). + + Бросает исключение при сетевой ошибке. Возвращает result-dict со status=OK. + + ВНИМАНИЕ: под высокой конкуренцией DeBank иногда отдаёт ответ от ДРУГОГО + адреса (response contamination) — такая выборка внутренне консистентна и + в одиночку неотличима от настоящей. Отсев делает корроборация в + check_wallet(): фантом случаен и не повторяется, истинный баланс стабилен. + """ + client = DeBankClient(proxy=proxy) + + # Токены в кошельке — фильтруем скам и неверифицированные + tokens = client.get_total_balance_cache(address) + tokens = [ + t for t in tokens + if t.get("is_verified", True) and not t.get("is_scam", False) + ] + + portfolio = client.get_portfolio(address) + + tokens_usd = sum(t.get("price", 0) * t.get("amount", 0) for t in tokens) + + protocols_data = [] + protocols_usd = 0.0 + for proto in portfolio: + proto_value = sum( + _safe_position_value(item) + for item in proto.get("portfolio_item_list", []) + ) + if round(proto_value, 2) < MIN_VALUE_DISPLAY: + continue + protocols_usd += proto_value + + positions = [] + for item in proto.get("portfolio_item_list", []): + net = _safe_position_value(item) + if round(net, 2) < MIN_VALUE_DISPLAY: + continue + detail = item.get("detail", {}) + supply = [ + f"{t.get('symbol', '?')} {t.get('amount', 0):.4f}" + for t in detail.get("supply_token_list", []) + ] + reward = [ + f"{t.get('symbol', '?')} {t.get('amount', 0):.4f}" + for t in detail.get("reward_token_list", []) + ] + positions.append({ + "type": item.get("name", ""), + "value": round(net, 2), + "supply": ", ".join(supply), + "rewards": ", ".join(reward), + }) + + protocols_data.append({ + "logo": proto.get("logo_url") or "", + "name": proto.get("name", "?"), + "chain": proto.get("chain", "?"), + "value": round(proto_value, 2), + "positions": positions, + }) + + protocols_data.sort(key=lambda p: p["value"], reverse=True) + + nft_usd = 0.0 + nft_data: list[dict] = [] + nft_chains: set[str] = set() + try: + nft_chains_list = client.get_nft_used_chains(address) + for chain in (nft_chains_list or [])[:10]: + collections = client.get_nft_collection_list(address, chain) + for col in collections: + amount = col.get("amount") or col.get("nft_count", 0) + if not amount: + continue + nft_chains.add(col.get("chain_id") or chain) + nft_data.append({ + "name": col.get("name", "?"), + "chain": col.get("chain_id") or chain, + "amount": amount, + }) + except Exception: + pass + nft_data.sort(key=lambda x: (x["chain"], x["name"])) + + total_usd = tokens_usd + protocols_usd + + # Лог сырых данных при аномалии (помогает увидеть, что именно отсеяла корроборация) + _write_log(address, total_usd, tokens_usd, protocols_usd, tokens, portfolio) + + chains = sorted({t.get("chain", "") for t in tokens if t.get("chain")} | nft_chains) + + tokens_data = [ + { + "logo": t.get("logo_url") or "", + "symbol": t.get("symbol", "?"), + "chain": t.get("chain", "?"), + "amount": t.get("amount", 0), + "price": t.get("price", 0), + "value": round(t.get("price", 0) * t.get("amount", 0), 2), + } + for t in tokens + if round(t.get("price", 0) * t.get("amount", 0), 2) >= MIN_VALUE_DISPLAY + ] + + top_tokens = sorted(tokens_data, key=lambda t: t["value"], reverse=True)[:3] + top_str = ", ".join(f"{t['symbol']}(${t['value']:.2f})" for t in top_tokens) + + return { + "address": address, + "total_usd": total_usd, + "tokens_usd": tokens_usd, + "protocols_usd": protocols_usd, + "nft_usd": nft_usd, + "tokens": len(tokens_data), + "chains": ", ".join(chains), + "top_tokens": top_str, + "tokens_data": tokens_data, + "protocols_data": protocols_data, + "nft_data": nft_data, + "proxy": proxy or "—", + "status": "OK", + "error": "", + "corroborated": True, + } + + +def check_wallet(address: str, proxy_manager: ProxyManager, wallet_idx: int = -1) -> dict[str, Any]: + """Проверяет баланс одного кошелька с защитой от «фантомных» балансов. + + Сетевые ошибки → повтор с новым прокси. При CORROBORATION_ENABLED баланс + принимается только если подтверждён CORROBORATION_MIN_AGREE независимыми + выборками, сошедшимися по total_usd; иначе берётся консервативное значение + (наибольшая согласованная группа, при равенстве — меньшая сумма). + """ + last_error = None + used_proxy = None + total_start = time.perf_counter() + + snapshots: list[dict[str, Any]] = [] + fetches = 0 + attempts = 0 + # Запас попыток на сетевые сбои поверх бюджета успешных выборок. + max_attempts = (max(RETRY_ATTEMPTS, CORROBORATION_MAX_FETCHES * 3) + if CORROBORATION_ENABLED else RETRY_ATTEMPTS) + + while attempts < max_attempts: + attempts += 1 + if CORROBORATION_ENABLED and fetches >= CORROBORATION_MAX_FETCHES: + break + + attempt_start = time.perf_counter() + proxy = proxy_manager.get_proxy() + if not proxy: + if not snapshots: + if DEBUG: + _debug_log(wallet_idx, address, "NO_PROXY", attempts, 0, "—") + return _error_result(address, "Нет доступных прокси", used_proxy) + break + used_proxy = proxy + + try: + snap = _fetch_snapshot(address, proxy) + except Exception as e: + last_error = e + err_str = str(e).lower() + if "timeout" in err_str or "timed out" in err_str: + proxy_manager.report_timeout(used_proxy) + if DEBUG: + _debug_log(wallet_idx, address, "FAIL", attempts, + time.perf_counter() - attempt_start, + _mask_proxy(used_proxy), str(e)[:80]) + continue + + fetches += 1 + + if not CORROBORATION_ENABLED: + if DEBUG: + _debug_log(wallet_idx, address, "OK", attempts, + time.perf_counter() - total_start, _mask_proxy(used_proxy)) + return snap + + # Корроборация: ищем группу согласованных по total_usd выборок. + snapshots.append(snap) + cluster = [s for s in snapshots if _agree(s["total_usd"], snap["total_usd"])] + if len(cluster) >= CORROBORATION_MIN_AGREE: + chosen = _rep(cluster) + chosen["corroborated"] = True + if DEBUG: + _debug_log(wallet_idx, address, "OK", attempts, + time.perf_counter() - total_start, _mask_proxy(used_proxy)) + return chosen + + # Бюджет исчерпан без подтверждения — берём консервативный результат. + if snapshots: + chosen = _rep(_largest_agreeing_cluster(snapshots)) + chosen["corroborated"] = False + chosen["error"] = (chosen.get("error") + or "баланс не подтверждён (возможен фантом) — взято консервативное значение") + if DEBUG: + others = sorted(round(s["total_usd"], 2) for s in snapshots) + _debug_log(wallet_idx, address, "WARN", attempts, + time.perf_counter() - total_start, _mask_proxy(used_proxy), + f"не подтверждён, выборки={others}") + return chosen + + if DEBUG: + elapsed = time.perf_counter() - total_start + _debug_log(wallet_idx, address, "ERROR", attempts, elapsed, + _mask_proxy(used_proxy), str(last_error)[:80]) + + return _error_result( + address, + str(last_error) if last_error else "Unknown error", + used_proxy, + ) + + +def _debug_log(idx: int, addr: str, status: str, attempt: int, sec: float, proxy: str, err: str = "") -> None: + """Отладочный лог в stderr.""" + if not DEBUG: + return + import sys + short = f"{addr[:10]}...{addr[-6:]}" if len(addr) > 20 else addr + err_part = f" | {err}" if err else "" + line = f"[DEBUG] #{idx} {short} | {status} | попытка {attempt} | {sec:.2f}s | {proxy}{err_part}\n" + sys.stderr.write(line) + sys.stderr.flush() + + +def _error_result(address: str, err_msg: str, proxy: str | None) -> dict[str, Any]: + return { + "address": address, + "total_usd": 0.0, + "tokens_usd": 0.0, + "protocols_usd": 0.0, + "nft_usd": 0.0, + "tokens": 0, + "chains": "", + "top_tokens": "", + "tokens_data": [], + "protocols_data": [], + "nft_data": [], + "proxy": proxy or "—", + "status": "ERROR", + "error": err_msg, + } \ No newline at end of file diff --git a/debank_checker/config.py b/debank_checker/config.py new file mode 100644 index 0000000..9b2cff6 --- /dev/null +++ b/debank_checker/config.py @@ -0,0 +1,48 @@ +""" +Конфигурация DeBank Balance Checker +""" + +# Пути к файлам +WALLETS_FILE = "wallets.txt" +PROXIES_FILE = "proxy.txt" +OUTPUT_DIR = "Results" # папка для Excel-результатов + +# API +REQUEST_TIMEOUT = 3 # секунд (быстрый failover при мёртвых прокси, см. docs/DEBUG_REPORT.md) +API_KEY_INIT = "3b92c003-ddc1-4c2d-b36e-781838f362c5" + +# Прокси и rate limit +RATE_LIMIT_REQ_PER_MIN = 60 # запросов в минуту на один прокси +RETRY_ATTEMPTS = 10 # попыток при ошибке (с новым прокси, без задержки между попытками) + +# Параллелизм +MAX_WORKERS = 500 # максимум одновременных воркеров +PROXY_MULTIPLIER = 5 # max_workers = min(MAX_WORKERS, len(wallets), len(proxies) * PROXY_MULTIPLIER) + +# Минимальная сумма для отображения (USD) +MIN_VALUE_DISPLAY = 0.01 + +# --- Защита от «фантомных» балансов ------------------------------------- +# Под высокой конкуренцией DeBank-API/edge иногда возвращает ОТВЕТ ОТ ДРУГОГО +# адреса (response contamination: ~1 на 30 запросов при 40+ воркерах; при +# последовательных запросах НЕ воспроизводится). Из-за этого на кошельке с $15 +# вдруг «появляются» сотни тысяч/миллионы. Данные при этом внутренне +# консистентны (верифицированные токены, реальные пулы) — пересчётом одной +# выборки фантом не отличить, нужна КОРРОБОРАЦИЯ независимыми запросами. +# +# Баланс принимается, только если подтверждён двумя независимыми выборками, +# сошедшимися в пределах допуска. Фантом случаен и не повторяется → отбрасываем; +# истинное значение стабильно → подтверждается. +CORROBORATION_ENABLED = True # False — старое поведение (1 запрос) +CORROBORATION_MIN_AGREE = 2 # сошедшихся выборок нужно для приёма +CORROBORATION_MAX_FETCHES = 8 # бюджет успешных выборок на кошелёк +CORROBORATION_REL_TOL = 0.02 # относительный допуск согласия (2%) +CORROBORATION_ABS_TOL = 1.0 # абсолютный допуск согласия (USD) + +# NFT +NFT_POLL_INTERVAL = 3 # сек между попытками при async job +NFT_POLL_MAX_WAIT = 30 # макс время ожидания результата +NFT_REQUEST_TIMEOUT = 3 # таймаут запроса (как REQUEST_TIMEOUT) + +# Отладка +DEBUG = False # True — отладочное логирование (время, попытки, прокси) diff --git a/debank_checker/export/__init__.py b/debank_checker/export/__init__.py new file mode 100644 index 0000000..355970c --- /dev/null +++ b/debank_checker/export/__init__.py @@ -0,0 +1,5 @@ +"""Экспорт: Excel.""" + +from debank_checker.export.excel import export_to_excel + +__all__ = ["export_to_excel"] diff --git a/debank_checker/export/common.py b/debank_checker/export/common.py new file mode 100644 index 0000000..68a9994 --- /dev/null +++ b/debank_checker/export/common.py @@ -0,0 +1,110 @@ +""" +Общая логика фильтрации и построения данных для экспорта +""" + +from debank_checker.export.config import ExportConfig + + +def filter_tokens(tokens_data: list[dict], token_filter: str | None) -> list[dict]: + """Фильтр по symbol:chain (например ETH:eth).""" + if not token_filter or ":" not in token_filter: + return tokens_data + parts = token_filter.split(":", 1) + symbol, chain = (parts[0].strip().upper(), parts[1].strip().lower()) if len(parts) == 2 else ("", "") + return [ + t for t in tokens_data + if (not symbol or (t.get("symbol") or "").upper() == symbol) + and (not chain or (t.get("chain") or "").lower() == chain) + ] + + +def filter_protocols(protocols_data: list[dict], protocol_filter: str | None) -> list[dict]: + """Фильтр по name:chain. :chain — все в сети.""" + if not protocol_filter or ":" not in protocol_filter: + return protocols_data + parts = protocol_filter.split(":", 1) + name, chain = (parts[0].strip(), parts[1].strip().lower()) if len(parts) == 2 else ("", "") + return [ + p for p in protocols_data + if (not name or (p.get("name") or "").strip() == name) + and (not chain or (p.get("chain") or "").lower() == chain) + ] + + +def filter_nft(nft_data: list[dict], nft_filter: str | None) -> list[dict]: + """Фильтр по collection:chain.""" + if not nft_filter or ":" not in nft_filter: + return nft_data + parts = nft_filter.split(":", 1) + collection, chain = (parts[0].strip(), parts[1].strip().lower()) if len(parts) == 2 else ("", "") + return [ + n for n in nft_data + if (not collection or collection.lower() in (n.get("name") or "").lower()) + and (not chain or (n.get("chain") or "").lower() == chain) + ] + + +def build_export_data(results: list[dict], config: ExportConfig) -> dict: + """ + Построить данные для экспорта по config. + Возвращает: { "total": {...}, "summary": [...], "tokens": [...], "protocols": [...], "nft": [...] } + """ + ok = [r for r in results if r and r.get("status") == "OK"] + total_sum = sum(r.get("total_usd", 0) for r in ok) + data = { + "total": {"sum_usd": total_sum, "ok_count": len(ok), "total_count": len(results)}, + "summary": [], + "tokens": [], + "protocols": [], + "nft": [], + } + + if config.summary and not config.total_only: + for i, r in enumerate(results, 1): + data["summary"].append({ + "n": i, + "address": r.get("address", ""), + "total_usd": r.get("total_usd") if r.get("status") == "OK" else None, + "chains": r.get("chains", ""), + "status": r.get("status", ""), + }) + + if config.tokens: + for r in results: + if r.get("status") != "OK": + continue + for t in filter_tokens(r.get("tokens_data", []), config.token_filter): + data["tokens"].append({ + "address": r["address"], + "symbol": t.get("symbol", ""), + "chain": t.get("chain", ""), + "amount": t.get("amount", 0), + "price": t.get("price", 0), + "value": t.get("value", 0), + }) + + if config.protocols: + for r in results: + if r.get("status") != "OK": + continue + for p in filter_protocols(r.get("protocols_data", []), config.protocol_filter): + data["protocols"].append({ + "address": r["address"], + "name": p.get("name", ""), + "chain": p.get("chain", ""), + "value": p.get("value", 0), + }) + + if config.nft: + for r in results: + if r.get("status") != "OK": + continue + for n in filter_nft(r.get("nft_data", []), config.nft_filter): + data["nft"].append({ + "address": r["address"], + "collection": n.get("name", ""), + "chain": n.get("chain", ""), + "amount": n.get("amount", 0), + }) + + return data diff --git a/debank_checker/export/config.py b/debank_checker/export/config.py new file mode 100644 index 0000000..9bd1fec --- /dev/null +++ b/debank_checker/export/config.py @@ -0,0 +1,65 @@ +""" +Конфигурация экспорта — что включать в результат +""" + +from dataclasses import dataclass + + +@dataclass +class ExportConfig: + """Настройки экспорта по выбору пользователя в меню.""" + + # 1 — только общий баланс + total_only: bool = False + # 2 — Summary по кошелькам + summary: bool = True + # 3 — конкретный токен (symbol:chain) + token_filter: str | None = None # "ETH:eth" или None + # 4 — все токены + tokens: bool = True + # 5 — все NFT + nft: bool = True + # 6 — конкретный NFT (collection:chain) + nft_filter: str | None = None # "Bored Ape:eth" или None + # 7 — DeFi-позиции (name:chain или :chain для всех в сети) + protocol_filter: str | None = None # "Uniswap:eth" или ":eth" + protocols: bool = True + + @classmethod + def from_menu_choice( + cls, + choice: int, + token_filter: str | None = None, + nft_filter: str | None = None, + protocol_filter: str | None = None, + ) -> "ExportConfig": + """Создать конфиг по номеру пункта меню (1–11).""" + configs = { + 1: cls(summary=True, tokens=False, nft=False, protocols=False), + 2: cls(summary=False, token_filter=token_filter or None, tokens=True, nft=False, protocols=False), + 3: cls(summary=False, tokens=True, nft=False, protocols=False), + 4: cls(summary=False, token_filter=token_filter or None, tokens=True, nft=False, protocols=False), + 5: cls(summary=False, tokens=False, nft=True, protocols=False), + 6: cls(summary=False, tokens=False, nft_filter=nft_filter or None, nft=True, protocols=False), + 7: cls(summary=False, tokens=False, nft_filter=nft_filter or None, nft=True, protocols=False), + 8: cls(summary=False, tokens=False, nft=False, protocol_filter=None, protocols=True), + 9: cls(summary=False, tokens=False, nft=False, protocol_filter=protocol_filter, protocols=True), + 10: cls(summary=False, tokens=False, nft=False, protocol_filter=protocol_filter or None, protocols=True), + 11: cls(summary=False, tokens=True, nft=True, protocols=True), + } + return configs.get(choice, configs[11]) + + def get_filename_suffix(self) -> str: + """Суффикс для имени файла: Total, Summary, Tokens, NFT, DeFi или комбинация.""" + if self.total_only: + return "Total" + if self.summary and not any([self.tokens, self.protocols, self.nft]): + return "Summary" + parts: list[str] = [] + if self.tokens: + parts.append("Tokens") + if self.protocols: + parts.append("DeFi") + if self.nft: + parts.append("NFT") + return "_".join(parts) if parts else "Summary" diff --git a/debank_checker/export/csv_exporter.py b/debank_checker/export/csv_exporter.py new file mode 100644 index 0000000..cdc5cf0 --- /dev/null +++ b/debank_checker/export/csv_exporter.py @@ -0,0 +1,69 @@ +""" +Экспорт результатов в CSV +""" + +import csv +import datetime +from pathlib import Path + +from debank_checker.export.common import build_export_data +from debank_checker.export.config import ExportConfig + + +def export_to_csv( + results: list[dict], + output_dir: Path | str = ".", + config: ExportConfig | None = None, +) -> Path: + """ + Экспорт в CSV по ExportConfig. + Создаёт один или несколько CSV-файлов в зависимости от config. + """ + config = config or ExportConfig(summary=True, tokens=True, nft=True, protocols=True) + output_dir = Path(output_dir) + ts = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") + suffix = config.get_filename_suffix() + base = f"debank_{suffix}_{ts}" + + data = build_export_data(results, config) + written: list[Path] = [] + + if config.total_only: + path = output_dir / f"{base}.csv" + with open(path, "w", encoding="utf-8", newline="") as f: + w = csv.writer(f) + w.writerow(["Параметр", "Значение"]) + w.writerow(["Суммарный баланс", data["total"]["sum_usd"]]) + w.writerow(["Успешно", f"{data['total']['ok_count']}/{data['total']['total_count']}"]) + written.append(path) + else: + if data["summary"]: + path = output_dir / f"{base}_summary.csv" + with open(path, "w", encoding="utf-8", newline="") as f: + w = csv.DictWriter(f, fieldnames=["n", "address", "total_usd", "chains", "status"]) + w.writeheader() + w.writerows(data["summary"]) + written.append(path) + if data["tokens"]: + path = output_dir / f"{base}_tokens.csv" + with open(path, "w", encoding="utf-8", newline="") as f: + w = csv.DictWriter(f, fieldnames=["address", "symbol", "chain", "amount", "price", "value"]) + w.writeheader() + w.writerows(data["tokens"]) + written.append(path) + if data["protocols"]: + path = output_dir / f"{base}_protocols.csv" + with open(path, "w", encoding="utf-8", newline="") as f: + w = csv.DictWriter(f, fieldnames=["address", "name", "chain", "value"]) + w.writeheader() + w.writerows(data["protocols"]) + written.append(path) + if data["nft"]: + path = output_dir / f"{base}_nft.csv" + with open(path, "w", encoding="utf-8", newline="") as f: + w = csv.DictWriter(f, fieldnames=["address", "collection", "chain", "amount"]) + w.writeheader() + w.writerows(data["nft"]) + written.append(path) + + return written[0] if written else output_dir / f"{base}.csv" diff --git a/debank_checker/export/excel.py b/debank_checker/export/excel.py new file mode 100644 index 0000000..a37494c --- /dev/null +++ b/debank_checker/export/excel.py @@ -0,0 +1,193 @@ +""" +Экспорт результатов в Excel +""" + +import datetime +from pathlib import Path + +from openpyxl import Workbook +from openpyxl.styles import Alignment, Border, Font, PatternFill, Side +from openpyxl.utils import get_column_letter + +from debank_checker.export.common import filter_nft, filter_protocols, filter_tokens +from debank_checker.export.config import ExportConfig + + +def export_to_excel( + results: list[dict], + output_dir: Path | str = ".", + config: ExportConfig | None = None, +) -> Path: + """ + Экспорт в Excel по ExportConfig. + Если config=None — экспорт всего (как раньше). + """ + config = config or ExportConfig(summary=True, tokens=True, nft=True, protocols=True) + output_dir = Path(output_dir) + suffix = config.get_filename_suffix() + filename = f"debank_{suffix}_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx" + filepath = output_dir / filename + + wb = Workbook() + + if config.total_only: + _add_total_only_sheet(wb, results) + else: + default_sheet = wb.active + if config.summary: + _style_summary_sheet(wb, results) + elif not any([config.tokens, config.protocols, config.nft]): + _style_summary_sheet(wb, results) + if config.tokens: + _add_tokens_sheet(wb, results, config.token_filter) + if config.protocols: + _add_protocols_sheet(wb, results, config.protocol_filter) + if config.nft: + _add_nft_sheet(wb, results, config.nft_filter) + if not config.summary and any([config.tokens, config.protocols, config.nft]) and default_sheet in wb.worksheets: + wb.remove(default_sheet) + + wb.save(filepath) + return filepath + + +def _add_total_only_sheet(wb: Workbook, results: list[dict]) -> None: + """Лист только с общим балансом.""" + ws = wb.active + ws.title = "Total" + total = sum(r["total_usd"] for r in results if r and r.get("status") == "OK") + ok = sum(1 for r in results if r and r.get("status") == "OK") + ws.cell(row=1, column=1, value="Суммарный баланс").font = Font(bold=True) + ws.cell(row=1, column=2, value=total).number_format = "#,##0.00" + ws.cell(row=2, column=1, value="Успешно") + ws.cell(row=2, column=2, value=f"{ok}/{len(results)}") + ws.column_dimensions["A"].width = 20 + ws.column_dimensions["B"].width = 18 + + +def _style_summary_sheet(wb: Workbook, results: list[dict]) -> None: + ws = wb.active + ws.title = "Summary" + + header_font = Font(name="Calibri", bold=True, color="FFFFFF", size=11) + header_fill = PatternFill("solid", fgColor="1A73E8") + ok_fill = PatternFill("solid", fgColor="E6F4EA") + err_fill = PatternFill("solid", fgColor="FCE8E6") + center = Alignment(horizontal="center", vertical="center") + left = Alignment(horizontal="left", vertical="center") + thin = Side(style="thin") + border = Border(left=thin, right=thin, top=thin, bottom=thin) + + headers = ["#", "Адрес", "USD баланс", "Chains", "Статус"] + for ci, h in enumerate(headers, 1): + c = ws.cell(row=1, column=ci, value=h) + c.font = header_font + c.fill = header_fill + c.alignment = center + c.border = border + ws.column_dimensions[get_column_letter(ci)].width = max(15, len(h) + 2) + + ws.column_dimensions["B"].width = 45 + ws.column_dimensions["C"].width = 18 + + for ri, r in enumerate(results, 2): + row_fill = ok_fill if r["status"] == "OK" else err_fill + values = [ + ri - 1, + r["address"], + r["total_usd"] if r["status"] == "OK" else None, + r.get("chains", ""), + r["status"], + ] + for ci, val in enumerate(values, 1): + c = ws.cell(row=ri, column=ci, value=val) + c.fill = row_fill + c.border = border + c.alignment = center if ci in (1, 3, 5) else left + if ci == 3 and isinstance(val, (int, float)): + c.number_format = "#,##0.00" + + total_sum = sum(x["total_usd"] for x in results if x["status"] == "OK") + ok_count = sum(1 for x in results if x["status"] == "OK") + summary_row = len(results) + 2 + ws.cell(row=summary_row, column=1, value="ИТОГО").font = Font(bold=True) + ws.cell(row=summary_row, column=2, value=f"{ok_count}/{len(results)} успешно") + sc = ws.cell(row=summary_row, column=3, value=total_sum) + sc.font = Font(bold=True) + sc.number_format = "#,##0.00" + + ws.freeze_panes = "A2" + + +def _add_tokens_sheet(wb: Workbook, results: list[dict], token_filter: str | None = None) -> None: + ws = wb.create_sheet("Tokens", 1) + headers = ["Адрес", "Symbol", "Chain", "Amount", "Price", "Value USD"] + for ci, h in enumerate(headers, 1): + ws.cell(row=1, column=ci, value=h).font = Font(bold=True) + ws.column_dimensions[get_column_letter(ci)].width = 15 + ws.column_dimensions["A"].width = 45 + + row = 2 + for r in results: + if r["status"] != "OK": + continue + tokens = filter_tokens(r.get("tokens_data", []), token_filter) + for t in tokens: + ws.cell(row=row, column=1, value=r["address"]) + ws.cell(row=row, column=2, value=t.get("symbol", "")) + ws.cell(row=row, column=3, value=t.get("chain", "")) + ws.cell(row=row, column=4, value=t.get("amount", 0)) + ws.cell(row=row, column=5, value=t.get("price", 0)) + ws.cell(row=row, column=6, value=t.get("value", 0)) + row += 1 + + if token_filter and row == 2: + ws.cell(row=2, column=1, value=f"Нет совпадений по фильтру {token_filter}. Проверьте symbol и chain.") + + +def _add_protocols_sheet(wb: Workbook, results: list[dict], protocol_filter: str | None = None) -> None: + ws = wb.create_sheet("Protocols", 2) + headers = ["Адрес", "Протокол", "Chain", "Value USD"] + for ci, h in enumerate(headers, 1): + ws.cell(row=1, column=ci, value=h).font = Font(bold=True) + ws.column_dimensions[get_column_letter(ci)].width = 20 + ws.column_dimensions["A"].width = 45 + + row = 2 + for r in results: + if r["status"] != "OK": + continue + protocols = filter_protocols(r.get("protocols_data", []), protocol_filter) + for p in protocols: + ws.cell(row=row, column=1, value=r["address"]) + ws.cell(row=row, column=2, value=p.get("name", "")) + ws.cell(row=row, column=3, value=p.get("chain", "")) + ws.cell(row=row, column=4, value=p.get("value", 0)) + row += 1 + + if protocol_filter and row == 2: + ws.cell(row=2, column=1, value=f"Нет совпадений по фильтру {protocol_filter}. Проверьте protocol и chain.") + + +def _add_nft_sheet(wb: Workbook, results: list[dict], nft_filter: str | None = None) -> None: + ws = wb.create_sheet("NFT", 3) + headers = ["Адрес", "Collection", "Chain", "Amount"] + for ci, h in enumerate(headers, 1): + ws.cell(row=1, column=ci, value=h).font = Font(bold=True) + ws.column_dimensions[get_column_letter(ci)].width = 20 + ws.column_dimensions["A"].width = 45 + + row = 2 + for r in results: + if r["status"] != "OK": + continue + nfts = filter_nft(r.get("nft_data", []), nft_filter) + for n in nfts: + ws.cell(row=row, column=1, value=r["address"]) + ws.cell(row=row, column=2, value=n.get("name", "")) + ws.cell(row=row, column=3, value=n.get("chain", "")) + ws.cell(row=row, column=4, value=n.get("amount", 0)) + row += 1 + + if nft_filter and row == 2: + ws.cell(row=2, column=1, value=f"Нет совпадений по фильтру {nft_filter}. Проверьте collection и chain.") diff --git a/debank_checker/export/json_exporter.py b/debank_checker/export/json_exporter.py new file mode 100644 index 0000000..717d23c --- /dev/null +++ b/debank_checker/export/json_exporter.py @@ -0,0 +1,48 @@ +""" +Экспорт результатов в JSON +""" + +import datetime +import json +from pathlib import Path + +from debank_checker.export.common import build_export_data +from debank_checker.export.config import ExportConfig + + +def _json_serial(obj): + """Сериализация для JSON (float, etc).""" + if isinstance(obj, float): + return round(obj, 6) if obj == obj else None # NaN check + raise TypeError(f"Object of type {type(obj)} is not JSON serializable") + + +def export_to_json( + results: list[dict], + output_dir: Path | str = ".", + config: ExportConfig | None = None, +) -> Path: + """ + Экспорт в JSON по ExportConfig. + Один файл со структурой { total, summary, tokens, protocols, nft }. + """ + config = config or ExportConfig(summary=True, tokens=True, nft=True, protocols=True) + output_dir = Path(output_dir) + ts = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") + suffix = config.get_filename_suffix() + filepath = output_dir / f"debank_{suffix}_{ts}.json" + + data = build_export_data(results, config) + out = { + "exported_at": datetime.datetime.now().isoformat(), + "total": data["total"], + "summary": data["summary"], + "tokens": data["tokens"], + "protocols": data["protocols"], + "nft": data["nft"], + } + + with open(filepath, "w", encoding="utf-8") as f: + json.dump(out, f, ensure_ascii=False, indent=2, default=_json_serial) + + return filepath diff --git a/debank_checker/proxy/__init__.py b/debank_checker/proxy/__init__.py new file mode 100644 index 0000000..cff54de --- /dev/null +++ b/debank_checker/proxy/__init__.py @@ -0,0 +1,5 @@ +"""Прокси: загрузка, ротация, rate limiting.""" + +from debank_checker.proxy.manager import ProxyManager, load_proxies + +__all__ = ["ProxyManager", "load_proxies"] diff --git a/debank_checker/proxy/manager.py b/debank_checker/proxy/manager.py new file mode 100644 index 0000000..531820e --- /dev/null +++ b/debank_checker/proxy/manager.py @@ -0,0 +1,116 @@ +""" +Менеджер прокси: загрузка, ротация, rate limiting +""" + +import threading +import time +from collections import deque +from pathlib import Path + +from debank_checker.config import DEBUG, PROXIES_FILE, RATE_LIMIT_REQ_PER_MIN + + +def load_proxies(path: str | Path = PROXIES_FILE) -> list[str]: + """ + Читает прокси из файла. + Формат: ip:port:login:password или ip:port + Возвращает список "http://login:password@ip:port" + """ + path = Path(path) + proxies = [] + if not path.exists(): + return proxies + with open(path, encoding="utf-8") as f: + for line in f: + line = line.strip() + if not line or line.startswith("#"): + continue + parts = line.split(":") + if len(parts) == 4: + ip, port, login, password = parts + proxies.append(f"http://{login}:{password}@{ip}:{port}") + elif len(parts) == 2: + proxies.append(f"http://{parts[0]}:{parts[1]}") + return proxies + + +class ProxyManager: + """ + Ротация прокси с учётом rate limit. + get_proxy() возвращает прокси с наименьшей нагрузкой. + report_timeout() временно исключает прокси из выдачи (см. docs/DEBUG_REPORT.md). + """ + + PROXY_COOLDOWN_AFTER_TIMEOUT = 60 # секунд, не выдавать прокси после таймаута + + def __init__( + self, + proxies: list[str], + req_per_min: int = RATE_LIMIT_REQ_PER_MIN, + ): + if not proxies: + self._proxies = [] + self._timestamps: dict[str, deque] = {} + self._timeout_until: dict[str, float] = {} + return + self._proxies = list(proxies) + self._req_per_min = req_per_min + self._lock = threading.Lock() + self._timestamps: dict[str, deque[float]] = { + p: deque(maxlen=req_per_min * 2) for p in self._proxies + } + self._timeout_until: dict[str, float] = {} # proxy -> time.time() когда снова доступен + self._idx = 0 + + def report_timeout(self, proxy: str | None) -> None: + """Исключить прокси из выдачи на PROXY_COOLDOWN_AFTER_TIMEOUT сек.""" + if not proxy: + return + with self._lock: + self._timeout_until[proxy] = time.time() + self.PROXY_COOLDOWN_AFTER_TIMEOUT + + def get_proxy(self) -> str | None: + """Возвращает прокси с наименьшей нагрузкой (round-robin с учётом rate limit).""" + if not self._proxies: + return None + t0 = time.perf_counter() + with self._lock: + now = time.time() + cutoff = now - 60 + best = None + best_count = float("inf") + for _ in range(len(self._proxies)): + p = self._proxies[self._idx % len(self._proxies)] + self._idx += 1 + if self._timeout_until.get(p, 0) > now: + continue + ts = self._timestamps[p] + while ts and ts[0] < cutoff: + ts.popleft() + if len(ts) < best_count and len(ts) < self._req_per_min: + best = p + best_count = len(ts) + break + forced = best is None + if best is None: + available = [p for p in self._proxies if self._timeout_until.get(p, 0) <= now] + best = available[0] if available else min( + self._proxies, key=lambda p: self._timeout_until.get(p, 0) + ) + self._timestamps[best].append(now) + result = best + if DEBUG: + elapsed = time.perf_counter() - t0 + load = len(self._timestamps[result]) + if elapsed > 0.01 or forced or load >= self._req_per_min: + import sys + short = result.split("@")[-1] if "@" in result else result + flags = [] + if forced: + flags.append("FORCED") + if load >= self._req_per_min: + flags.append(f"LOAD={load}") + flag_str = " | " + ", ".join(flags) if flags else "" + sys.stderr.write(f"[DEBUG] get_proxy | {elapsed:.3f}s | {short}{flag_str}\n") + sys.stderr.flush() + return result diff --git a/debank_checker/ui/__init__.py b/debank_checker/ui/__init__.py new file mode 100644 index 0000000..06fcadf --- /dev/null +++ b/debank_checker/ui/__init__.py @@ -0,0 +1,6 @@ +"""UI: баннер, логирование.""" + +from debank_checker.ui.banner import create_progress_bar, show +from debank_checker.ui.logger import error, info, success, warning + +__all__ = ["create_progress_bar", "show", "error", "info", "success", "warning"] diff --git a/debank_checker/ui/banner.py b/debank_checker/ui/banner.py new file mode 100644 index 0000000..e35bbf5 --- /dev/null +++ b/debank_checker/ui/banner.py @@ -0,0 +1,85 @@ +""" +ASCII-заставка в стиле Season7 +""" + +import sys + +# ANSI цвета +RESET = "\033[0m" +BRIGHT = "\033[1m" +DIM = "\033[2m" +CYAN = "\033[36m" +YELLOW = "\033[33m" +BLUE = "\033[34m" +GREEN = "\033[32m" + +_BANNER_INDENT = " " +_BANNER_LINES = [ + "██████╗ ███████╗██████╗ █████╗ ███╗ ██╗██╗ ██╗", + "██╔══██╗██╔════╝██╔══██╗██╔══██╗████╗ ██║██║ ██╔╝", + "██║ ██║█████╗ ██████╔╝███████║██╔██╗ ██║█████╔╝", + "██║ ██║██╔══╝ ██╔══██╗██╔══██║██║╚██╗██║██╔═██╗", + "██████╔╝███████╗██████╔╝██║ ██║██║ ╚████║██║ ██╗", + "╚═════╝ ╚══════╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝", +] +_BANNER_WIDTH = max(len(l) for l in _BANNER_LINES) + + +def _center_text_in_banner(s: str) -> str: + """Центрировать строку относительно ширины ASCII-баннера (не экрана).""" + s = s.rstrip() + pad = max(0, (_BANNER_WIDTH - len(s)) // 2) + return f"{_BANNER_INDENT}{' ' * pad}{s}" + + +def show() -> None: + """Показать главную заставку.""" + if sys.platform == "win32": + try: + import os + os.system("cls") + except Exception: + pass + else: + print("\033[2J\033[H", end="") + + print(create_main_banner()) + print(create_subtitle()) + print(create_separator()) + print() + + +def create_main_banner() -> str: + """Главный баннер DeBank Checker.""" + banner = "\n".join(f"{_BANNER_INDENT}{l}" for l in _BANNER_LINES) + return f"\n{CYAN}{BRIGHT}{banner}{RESET}" + + +def create_subtitle() -> str: + """Подзаголовок.""" + return "\n".join( + [ + _center_text_in_banner(f"{YELLOW}{BRIGHT}BALANCE CHECKER v1.1.0{RESET}"), + _center_text_in_banner(f"{DIM}EVM · Tokens · DeFi · NFT{RESET}"), + _center_text_in_banner(f"{DIM}TG: https://t.me/privatekey_ai{RESET}"), + ] + ) + + +def create_separator() -> str: + """Разделитель.""" + return f"{_BANNER_INDENT}{BLUE}{'=' * _BANNER_WIDTH}{RESET}" + + +def create_progress_bar(current: int, total: int, width: int = 50) -> str: + """Прогресс-бар в стиле Season7.""" + if total <= 0: + pct = 0 + filled = 0 + empty = width + else: + pct = round((current / total) * 100) + filled = round((current / total) * width) + empty = width - filled + bar = f"{GREEN}{'█' * filled}{DIM}{'░' * empty}{RESET}" + return f"[{bar}] {pct}% ({current}/{total})" diff --git a/debank_checker/ui/logger.py b/debank_checker/ui/logger.py new file mode 100644 index 0000000..15131ba --- /dev/null +++ b/debank_checker/ui/logger.py @@ -0,0 +1,40 @@ +""" +Логирование в формате: YYYY-MM-DD HH:MM:SS | LEVEL | Сообщение +Цвета: INFO — белый, WARNING — оранжевый, SUCCESS — зелёный, ERROR — красный +""" + +import sys +from datetime import datetime + +# ANSI коды +RESET = "\033[0m" +WHITE = "\033[37m" +BRIGHT = "\033[1m" +ORANGE = "\033[38;5;208m" # 256-color orange +GREEN = "\033[32m" +RED = "\033[31m" + + +def _timestamp() -> str: + return datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + +def _log(level: str, msg: str, color: str) -> None: + line = f"{_timestamp()} | {level:<7} | {msg}" + print(f"{color}{line}{RESET}", file=sys.stderr, flush=True) + + +def info(msg: str) -> None: + _log("INFO", msg, WHITE + BRIGHT) + + +def warning(msg: str) -> None: + _log("WARNING", msg, ORANGE + BRIGHT) + + +def success(msg: str) -> None: + _log("SUCCESS", msg, GREEN + BRIGHT) + + +def error(msg: str) -> None: + _log("ERROR", msg, RED + BRIGHT) diff --git a/debank_checker/ui/menu.py b/debank_checker/ui/menu.py new file mode 100644 index 0000000..3589f17 --- /dev/null +++ b/debank_checker/ui/menu.py @@ -0,0 +1,273 @@ +""" +Консольное меню выбора экспорта. +Вызывается после проверки кошельков — данные уже получены. +Двухшаговый выбор: сначала сеть, затем токен/NFT/протокол в этой сети. +""" + +import questionary + +from debank_checker.export.config import ExportConfig + +# Разделитель для фильтра (symbol:chain) +_SEP = ":" +_ALL_IN_CHAIN = "← Все в этой сети" + + +def _get_chains_from_tokens(results: list[dict] | None) -> list[str]: + """Уникальные сети из токенов.""" + chains: set[str] = set() + if not results: + return [] + for r in results: + if r.get("status") != "OK": + continue + for t in r.get("tokens_data", []): + ch = (t.get("chain") or "").strip().lower() + if ch: + chains.add(ch) + return sorted(chains) + + +def _get_chains_from_nft(results: list[dict] | None) -> list[str]: + """Уникальные сети из NFT.""" + chains: set[str] = set() + if not results: + return [] + for r in results: + if r.get("status") != "OK": + continue + for n in r.get("nft_data", []): + ch = (n.get("chain") or "").strip().lower() + if ch: + chains.add(ch) + return sorted(chains) + + +def _get_chains_from_protocols(results: list[dict] | None) -> list[str]: + """Уникальные сети из DeFi-протоколов.""" + chains: set[str] = set() + if not results: + return [] + for r in results: + if r.get("status") != "OK": + continue + for p in r.get("protocols_data", []): + ch = (p.get("chain") or "").strip().lower() + if ch: + chains.add(ch) + return sorted(chains) + + +def _build_token_options_for_chain(results: list[dict] | None, chain: str) -> list[str]: + """Токены в указанной сети: SYMBOL (из реальных данных).""" + seen: set[str] = set() + options: list[str] = [] + if not results: + return options + chain_l = chain.lower() + for r in results: + if r.get("status") != "OK": + continue + for t in r.get("tokens_data", []): + if (t.get("chain") or "").lower() != chain_l: + continue + symbol = (t.get("symbol") or "").strip().upper() + if symbol and symbol not in seen: + seen.add(symbol) + options.append(symbol) + return sorted(options) + + +def _build_nft_options_for_chain(results: list[dict] | None, chain: str) -> list[str]: + """NFT-коллекции в указанной сети (из реальных данных).""" + seen: set[str] = set() + options: list[str] = [] + if not results: + return options + chain_l = chain.lower() + for r in results: + if r.get("status") != "OK": + continue + for n in r.get("nft_data", []): + if (n.get("chain") or "").lower() != chain_l: + continue + name = (n.get("name") or "").strip() + if name and name not in seen: + seen.add(name) + options.append(name) + return sorted(options) + + +def _build_protocol_options_for_chain(results: list[dict] | None, chain: str) -> list[str]: + """DeFi-протоколы в указанной сети (из реальных данных).""" + seen: set[str] = set() + options: list[str] = [] + if not results: + return options + chain_l = chain.lower() + for r in results: + if r.get("status") != "OK": + continue + for p in r.get("protocols_data", []): + if (p.get("chain") or "").lower() != chain_l: + continue + name = (p.get("name") or "").strip() + if name and name not in seen: + seen.add(name) + options.append(name) + return sorted(options) + + +def _select(title: str, options: list[str]) -> str | None: + """Выбор из списка с помощью questionary.""" + if not options: + return None + result = questionary.select(title, choices=options).ask() + return result + + +FORMAT_OPTIONS = [ + "CSV", + "Excel", + "JSON", +] + +AGAIN_OPTIONS = [ + "1. Продолжить", + "2. Выйти", +] + +MAIN_OPTIONS = [ + "1. Summary (адрес, баланс, chains, статус)", + "2. Конкретный токен (сеть → токен)", + "3. Все токены", + "4. Все токены в конкретной сети", + "5. Все NFT", + "6. Конкретный NFT (сеть → коллекция)", + "7. Все NFT в конкретной сети", + "8. Все DeFi-позиции", + "9. Конкретный DeFi (сеть → протокол)", + "10. Все DeFi в конкретной сети", + "11. Всё (Tokens + Protocols + NFT)", +] + + +def show_menu(results: list[dict] | None = None) -> ExportConfig: + """ + Интерактивное меню: двухшаговый выбор. + Шаг 1: выбор сети. Шаг 2: выбор токена/NFT/протокола в этой сети. + """ + choice_str = _select("Что экспортировать? (↑↓ — выбор, Enter — подтвердить)", MAIN_OPTIONS) + if not choice_str: + choice_str = MAIN_OPTIONS[0] + choice = int(choice_str.split(".")[0].strip()) + + token_filter = None + nft_filter = None + protocol_filter = None + + if choice == 2: + chains = _get_chains_from_tokens(results) + if not chains: + print(" Нет токенов в данных. Экспорт всех токенов (п.3).") + choice = 3 + else: + chain = _select("Шаг 1: Выберите сеть", chains) + if not chain: + choice = 3 + else: + tokens = _build_token_options_for_chain(results, chain) + if not tokens: + print(" Нет токенов в этой сети. Экспорт всех токенов (п.3).") + choice = 3 + else: + symbol = _select("Шаг 2: Выберите токен", tokens) + if symbol: + token_filter = f"{symbol}{_SEP}{chain}" + + elif choice == 4: + chains = _get_chains_from_tokens(results) + if not chains: + print(" Нет токенов в данных. Экспорт всех токенов (п.3).") + choice = 3 + else: + chain = _select("Выберите сеть", chains) + if chain: + token_filter = f"{_SEP}{chain}" + + elif choice == 6: + chains = _get_chains_from_nft(results) + if not chains: + print(" Нет NFT в данных. Экспорт всех NFT (п.5).") + choice = 5 + else: + chain = _select("Шаг 1: Выберите сеть", chains) + if not chain: + choice = 5 + else: + nfts = _build_nft_options_for_chain(results, chain) + if not nfts: + print(" Нет NFT в этой сети. Экспорт всех NFT (п.5).") + choice = 5 + else: + collection = _select("Шаг 2: Выберите NFT-коллекцию", nfts) + if collection: + nft_filter = f"{collection}{_SEP}{chain}" + + elif choice == 7: + chains = _get_chains_from_nft(results) + if not chains: + print(" Нет NFT в данных. Экспорт всех NFT (п.5).") + choice = 5 + else: + chain = _select("Выберите сеть", chains) + if chain: + nft_filter = f"{_SEP}{chain}" + + elif choice == 9: + chains = _get_chains_from_protocols(results) + if not chains: + print(" Нет DeFi-позиций в данных. Экспорт всех DeFi (п.8).") + choice = 8 + else: + chain = _select("Шаг 1: Выберите сеть", chains) + if not chain: + choice = 8 + else: + protocols = _build_protocol_options_for_chain(results, chain) + if not protocols: + print(" Нет протоколов в этой сети. Экспорт всех DeFi (п.8).") + choice = 8 + else: + options = [_ALL_IN_CHAIN] + protocols + selected = _select("Шаг 2: Выберите протокол или «Все в этой сети»", options) + if selected == _ALL_IN_CHAIN: + protocol_filter = f"{_SEP}{chain}" + elif selected: + protocol_filter = f"{selected}{_SEP}{chain}" + + elif choice == 10: + chains = _get_chains_from_protocols(results) + if not chains: + print(" Нет DeFi-позиций в данных. Экспорт всех DeFi (п.8).") + choice = 8 + else: + chain = _select("Выберите сеть", chains) + if chain: + protocol_filter = f"{_SEP}{chain}" + + return ExportConfig.from_menu_choice(choice, token_filter, nft_filter, protocol_filter) + + +def ask_format() -> str: + """Выбор формата экспорта: csv, excel, json.""" + choice = _select("В какой формат экспортировать?", FORMAT_OPTIONS) + if not choice: + return "excel" + return choice.lower() + + +def ask_continue() -> bool: + """Спросить: продолжить или выйти. True = продолжить.""" + choice = _select("Продолжить?", AGAIN_OPTIONS) + return choice == AGAIN_OPTIONS[0] if choice else False diff --git a/main.py b/main.py index ee20995..f8f6843 100644 --- a/main.py +++ b/main.py @@ -1,4 +1,146 @@ -from app.main import main +""" +DeBank Balance Checker — точка входа +""" + +import sys +import threading +import time +from concurrent.futures import ThreadPoolExecutor, as_completed +from pathlib import Path + +from debank_checker.checker import check_wallet +from debank_checker.config import ( + DEBUG, + MAX_WORKERS, + OUTPUT_DIR, + PROXIES_FILE, + PROXY_MULTIPLIER, + WALLETS_FILE, +) +from debank_checker.export.csv_exporter import export_to_csv +from debank_checker.export.excel import export_to_excel +from debank_checker.export.json_exporter import export_to_json +from debank_checker.proxy.manager import load_proxies, ProxyManager +from debank_checker.ui.banner import create_progress_bar, show +from debank_checker.ui.logger import error, info, success +from debank_checker.ui.menu import ask_continue, ask_format, show_menu + +def setup_encoding() -> None: + """UTF-8 и colorama для Windows.""" + if sys.platform == "win32": + try: + import colorama + colorama.init() + except ImportError: + pass + try: + sys.stdout.reconfigure(encoding="utf-8", errors="replace") + sys.stderr.reconfigure(encoding="utf-8", errors="replace") + except Exception: + pass + + +def load_wallets(path: str | Path = WALLETS_FILE) -> list[str]: + """Читает адреса из файла.""" + path = Path(path) + wallets = [] + if not path.exists(): + return wallets + with open(path, encoding="utf-8") as f: + for line in f: + line = line.strip() + if line and not line.startswith("#"): + wallets.append(line) + return wallets + + +def main() -> None: + setup_encoding() + show() + + wallets = load_wallets() + proxies = load_proxies() + + if not wallets: + error("Нет кошельков для проверки. Добавь адреса в wallets.txt") + sys.exit(1) + + if not proxies: + error("Прокси обязательны. Добавь прокси в proxy.txt") + sys.exit(1) + + info(f"Кошельков: {len(wallets)} | Прокси: {len(proxies)}") + + max_workers = min(MAX_WORKERS, len(wallets), len(proxies) * PROXY_MULTIPLIER) + max_workers = max(1, max_workers) + info(f"Параллельных воркеров: {max_workers}") + + proxy_manager = ProxyManager(proxies) + results: list[dict] = [None] * len(wallets) + completed = [0] + lock = threading.Lock() + + def process(idx: int, address: str) -> None: + start = time.perf_counter() + row = check_wallet(address, proxy_manager, wallet_idx=idx) + results[idx] = row + if DEBUG: + elapsed = time.perf_counter() - start + sys.stderr.write(f"[DEBUG] #{idx} DONE | {elapsed:.2f}s total | status={row['status']}\n") + sys.stderr.flush() + with lock: + completed[0] += 1 + done = completed[0] + total = len(wallets) + bar = create_progress_bar(done, total) + print(f"\r{bar}", end="", flush=True) + + print("\r" + create_progress_bar(0, len(wallets)), end="", flush=True) + + with ThreadPoolExecutor(max_workers=max_workers) as executor: + futures = { + executor.submit(process, i, w): i + for i, w in enumerate(wallets) + } + for future in as_completed(futures): + future.result() + + print() + + ok_count = sum(1 for r in results if r and r["status"] == "OK") + total_sum = sum(r["total_usd"] for r in results if r and r["status"] == "OK") + + info(f"Итого: {ok_count}/{len(results)} успешно | Суммарный баланс: ${total_sum:,.2f}") + + output_path = Path(OUTPUT_DIR) + output_path.mkdir(parents=True, exist_ok=True) + + while True: + export_config = show_menu(results) + fmt = ask_format() + + if fmt == "csv": + out_path = export_to_csv(results, output_path, config=export_config) + success(f"CSV сохранён: {out_path.resolve()}") + elif fmt == "json": + out_path = export_to_json(results, output_path, config=export_config) + success(f"JSON сохранён: {out_path.resolve()}") + else: + out_path = export_to_excel(results, output_path, config=export_config) + success(f"Excel сохранён: {out_path.resolve()}") + + if not ask_continue(): + break + + info("Готово.") + if __name__ == "__main__": - main() + try: + main() + except KeyboardInterrupt: + info("Прервано пользователем") + sys.exit(0) + except Exception as e: + error(f"Критическая ошибка: {e}") + sys.exit(1) diff --git a/requirements.txt b/requirements.txt index 131f199..6014d71 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,5 @@ -PySide6>=6.6 -httpx[socks]>=0.27 -openpyxl>=3.1 -PyYAML>=6.0 -python-dotenv>=1.0 -web3>=6.0,<8 -solana>=0.34 -curl-cffi>=0.7 +curl_cffi>=0.5.0 +openpyxl>=3.1.0 +colorama>=0.4.6 +questionary>=2.0.0 +pytest>=7.0