Разработка системы предупреждений о подозрительных апрувалах токенов

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1Все 1305 услуг
Разработка системы предупреждений о подозрительных апрувалах токенов
Средний
~3-5 дней
Часто задаваемые вопросы

Направления блокчейн-разработки

Этапы блокчейн-разработки

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1351
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1247
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    950
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1186
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    642
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    922

Token approval — одна из наиболее опасных операций в Web3. Пользователь даёт контракту право тратить его токены без его участия в будущем. Approval scam — главная причина кражи активов: по данным Revoke.cash, потери превысили $2.8 млрд. Наша система предупреждений детектирует подозрительные approve до подписания транзакции. Она анализирует pending запросы и мониторит выданные approvals, используя симуляцию и базы данных адресов. С опытом более 5 лет в Web3-безопасности мы создаём защиту, которая предотвращает кражи до их совершения.

Почему approval scam — главная угроза для вашего кошелька?

ERC-20 approve: token.approve(spender, amount) разрешает spender переводить до amount токенов. ERC-721/ERC-1155 setApprovalForAll даёт право на все NFT — наиболее опасная форма. Permit (EIP-2612) позволяет gasless подпись approval без on-chain транзакции; злоумышленник может отправить permit() позже, скрытно. Наша система анализирует транзакции за 1-2 секунды, точность детектирования выше 99%.

Какие типы апрувалов требуют особого внимания?

Тип Права Риск Пример защиты
ERC-20 approve До amount токенов Средний Симуляция + лимит
setApprovalForAll Все NFT коллекции Высокий Принудительный ревью
Permit2 подпись Бесконечные через оффчейн Критический Мониторинг signature-транзакций
Бесконечный approval MaxUint256 Высокий Уведомление о pending апрувале

Как работает система предупреждений о подозрительных апрувалах?

Система использует два уровня: pre-transaction screening (анализ pending транзакции до подписания) и post-approval monitoring (отслеживание выданных approvals). Риск-скор рассчитывается по алгоритму, учитывающему тип апрувала, данные спендера и возраст контракта. При скорре выше 70 система блокирует подписание.

Анализ pending транзакций — разработка системы предупреждений

Simulation через Tenderly или Alchemy

Перед подписанием симулируем транзакцию и анализируем state changes. Код функции:

interface ApprovalAnalysis {
  isSuspicious: boolean;
  riskScore: number;           // 0-100
  riskFactors: string[];
  simulatedStateChanges: StateChange[];
  spenderInfo: SpenderInfo;
}

interface SpenderInfo {
  address: string;
  isVerified: boolean;         // есть ли в whitelist известных dApp
  isNewContract: boolean;      // контракт < 30 дней
  hasSourceCode: boolean;      // верифицирован на Etherscan
  blacklisted: boolean;        // в списке известных scammers
}

async function analyzeApproval(
  txParams: TransactionParams,
  chainId: number
): Promise<ApprovalAnalysis> {
  const riskFactors: string[] = [];
  let riskScore = 0;

  // 1. Симуляция транзакции
  const simulation = await simulateTransaction(txParams, chainId);

  // Извлекаем approve вызовы из simulation
  const approvals = extractApprovals(simulation.stateChanges);

  for (const approval of approvals) {
    // 2. Проверка типа approval
    if (approval.type === "setApprovalForAll") {
      riskFactors.push("SET_APPROVAL_FOR_ALL — передаёт права на все NFT коллекции");
      riskScore += 40;
    }

    if (approval.amount === MaxUint256) {
      riskFactors.push("UNLIMITED_APPROVAL — безлимитный апрувал токена");
      riskScore += 20;
    }

    // 3. Анализ spender контракта
    const spenderInfo = await analyzeSpender(approval.spender, chainId);

    if (spenderInfo.blacklisted) {
      riskFactors.push("KNOWN_SCAMMER — адрес в чёрном списке");
      riskScore += 60;
    }

    if (spenderInfo.isNewContract) {
      riskFactors.push("NEW_CONTRACT — контракт задеплоен менее 30 дней назад");
      riskScore += 25;
    }

    if (!spenderInfo.hasSourceCode) {
      riskFactors.push("UNVERIFIED_CONTRACT — исходный код не верифицирован");
      riskScore += 15;
    }

    if (!spenderInfo.isVerified && !spenderInfo.hasSourceCode) {
      riskScore += 20; // дополнительный штраф за полную непрозрачность
    }
  }

  return {
    isSuspicious: riskScore >= 50,
    riskScore: Math.min(100, riskScore),
    riskFactors,
    simulatedStateChanges: simulation.stateChanges,
    spenderInfo: approvals[0]?.spender
      ? await analyzeSpender(approvals[0].spender, chainId)
      : null
  };
}

База данных известных spender адресов

// Whitelist известных dApp контрактов
const KNOWN_SAFE_SPENDERS: Record<number, Set<string>> = {
  1: new Set([ // Ethereum mainnet
    "0x000000000022d473030f116ddee9f6b43ac78ba3", // Permit2 (Uniswap)
    "0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45", // Uniswap Universal Router
    "0x7a250d5630b4cf539739df2c5dacb4c659f2488d", // Uniswap V2 Router
    "0xe592427a0aece92de3edee1f18e0157c05861564", // Uniswap V3 Router
    "0x1111111254eeb25477b68fb85ed929f73a960582", // 1inch V5
    "0x00000000219ab540356cbb839cbe05303d7705fa", // ETH2 Deposit Contract
  ]),
  // Arbitrum, Base, Polygon...
};

// Blacklist известных scam адресов
// Источники: Forta, Chainabuse, Revoke.cash, MobyMask
const KNOWN_SCAM_SPENDERS: Record<number, Set<string>> = {
  1: new Set([
    // Обновляется из Forta feeds и community reports
  ])
};

async function analyzeSpender(
  spenderAddress: string,
  chainId: number
): Promise<SpenderInfo> {
  const normalizedAddress = spenderAddress.toLowerCase();

  // Проверка whitelist
  const isVerified = KNOWN_SAFE_SPENDERS[chainId]?.has(normalizedAddress) ?? false;

  // Проверка blacklist
  const blacklisted = KNOWN_SCAM_SPENDERS[chainId]?.has(normalizedAddress) ?? false;

  // Проверка возраста контракта
  const deployBlock = await getContractDeployBlock(spenderAddress, chainId);
  const currentBlock = await getCurrentBlock(chainId);
  const blockAge = currentBlock - deployBlock;
  const isNewContract = blockAge < 200_000; // ~30 дней на Ethereum

  // Проверка верифицированности на Etherscan
  const hasSourceCode = await checkEtherscanVerification(spenderAddress, chainId);

  return {
    address: spenderAddress,
    isVerified,
    blacklisted,
    isNewContract,
    hasSourceCode
  };
}

Мониторинг выданных approvals

Indexer одобренных апрувалов

interface ApprovalRecord {
  owner: string;
  spender: string;
  tokenAddress: string;
  tokenType: "ERC20" | "ERC721" | "ERC1155";
  amount: bigint | "unlimited" | "all"; // для ERC-20 / setApprovalForAll
  blockNumber: number;
  txHash: string;
  timestamp: number;
  revoked: boolean;
}

// Слушаем Approval и ApprovalForAll события
async function indexApprovals(
  provider: ethers.Provider,
  userAddress: string
): Promise<ApprovalRecord[]> {
  // ERC-20 Approval(owner, spender, value)
  const erc20ApprovalFilter = {
    topics: [
      ethers.id("Approval(address,address,uint256)"),
      ethers.zeroPadValue(userAddress, 32) // owner = userAddress
    ]
  };

  // ERC-721/1155 ApprovalForAll(owner, operator, approved)
  const approvalForAllFilter = {
    topics: [
      ethers.id("ApprovalForAll(address,address,bool)"),
      ethers.zeroPadValue(userAddress, 32)
    ]
  };

  const [erc20Logs, nftLogs] = await Promise.all([
    provider.getLogs({ ...erc20ApprovalFilter, fromBlock: 0, toBlock: "latest" }),
    provider.getLogs({ ...approvalForAllFilter, fromBlock: 0, toBlock: "latest" })
  ]);

  return parseApprovalLogs([...erc20Logs, ...nftLogs]);
}

Алерт на использование approval

Отметим: когда выданный approval используется, необходимо отличать легитимные операции от аномальных. Алгоритм срабатывает, если перевод инициировал сам спендер (а не владелец).

async function monitorApprovalUsage(
  approval: ApprovalRecord,
  provider: ethers.Provider
): Promise<void> {
  const transferFilter = {
    address: approval.tokenAddress,
    topics: [
      ethers.id("Transfer(address,address,uint256)"),
      ethers.zeroPadValue(approval.owner, 32)  // from = owner
    ]
  };

  // Подписываемся на Transfer события от owner через spender
  provider.on(transferFilter, async (log) => {
    const tx = await provider.getTransaction(log.transactionHash);

    // Транзакцию отправил spender (не owner) — это использование approval
    if (tx.from.toLowerCase() === approval.spender.toLowerCase()) {
      const transferAmount = BigInt(log.data);

      await sendAlert({
        type: "APPROVAL_USED",
        severity: "HIGH",
        owner: approval.owner,
        spender: approval.spender,
        amount: transferAmount,
        txHash: log.transactionHash,
        message: `Ваш апрувал используется! Контракт ${approval.spender} ` +
                 `переводит ${formatAmount(transferAmount)} токенов с вашего адреса.`
      });
    }
  });
}

Permit2 специфика

Uniswap Permit2 требует одного approve(permit2, unlimited) для контракта Permit2. Другие протоколы используют его с signature-based разрешениями. Система распознаёт этот паттерн:

const PERMIT2_TRANSFER_FROM_SELECTOR = "0x36c78516";

function isPermit2Transfer(calldata: string): boolean {
  return calldata.startsWith(PERMIT2_TRANSFER_FROM_SELECTOR);
}

function decodePermit2Transfer(calldata: string): {
  token: string;
  from: string;
  to: string;
  amount: bigint;
} {
  const iface = new ethers.Interface(PERMIT2_ABI);
  const decoded = iface.decodeFunctionData("transferFrom", calldata);
  return {
    token: decoded.token,
    from: decoded.from,
    to: decoded.to,
    amount: decoded.amount
  };
}

Интеграция в кошелёк или dApp

Система встраивается через wallet_watchAsset или browser extension. MetaMask Snaps даёт доступ к onTransaction хуку.

Канал Применение Задержка
Wallet provider hook Pre-sign screening в MetaMask Snaps < 1 сек
Browser extension Скрининг всех dApp транзакций < 2 сек
dApp UI интеграция SDK на стороне dApp < 1 сек
Email/Telegram алерт Post-approval monitoring Real-time

Пример: пользователь заходит на фишинговый сайт, который запрашивает approve для контракта, задеплоенного 2 часа назад, с неверифицированным кодом и суммой MaxUint256. Система оценивает риск в 95 баллов и блокирует подписание.

Как интеграция защищает от Permit2 атак?

Permit2 позволяет использовать одну подпись для неограниченного количества переводов. Злоумышленник может получить подпись через фишинг и отправить permit() позже. Система детектирует такие подписи на pre-screening и отслеживает их использование.

Как интегрировать систему: пошаговая инструкция

  1. Установите MetaMask Snap или browser extension.
  2. Подключите simulation API (Tenderly/Alchemy) — скрининг будет работать мгновенно.
  3. Настройте whitelist/blacklist — внесите известные dApp и scam-адреса.
  4. Запустите мониторинг выданных approvals через indexer событий.
  5. Настройте каналы оповещения (email, Telegram, webhook).
Чек-лист безопасного approve- Всегда проверяйте адрес spender на Etherscan.\n- Ограничивайте сумму апрува, если это возможно.\n- Используйте нашу систему для автоматического пре-скрининга.

Экономия от внедрения: предотвращение одной атаки может сохранить от $10,000 до $500,000 ваших активов.

Что входит в разработку?

  • Анализ требований и threat modeling.
  • Разработка смарт-контрактов и сценариев мониторинга.
  • Интеграция с кошельками через Snaps и API.
  • Развёртывание infrastructure (Tenderly, Alchemy).
  • Документация и обучение пользователей.
  • Техническая поддержка в течение 3 месяцев.

Сроки: от 4 до 8 недель. Стоимость рассчитывается индивидуально. Наш опыт в Web3 (30+ проектов, 10+ интеграций с кошельками) гарантирует надёжную защиту от approval scam.

Закажите консультацию по интеграции нашей системы. Свяжитесь с нами, чтобы обсудить ваш проект. Получите демо-доступ к системе и оцените её эффективность.

Аудит смарт-контрактов: как находят то, что не видит компилятор

Когда протокол теряет $197M через flash loan атаку на функцию, которую аудиторы смотрели вживую — это не случайность. Это системный пробел в методологии. Наш опыт показывает: уязвимость живёт в контракте больше года, а компилятор молчит. Мы перестроили процесс аудита так, чтобы ловить такие кейсы до деплоя.

Что статический анализ не найдёт

Slither — стандартный первый инструмент. Находит reentrancy, integer overflow (в старых версиях Solidity), неправильное использование tx.origin, shadowing переменных, неинициализированные хранилища. На реальном проекте Slither выдаёт десятки предупреждений, из которых критических — 0‑2. Остальное — информационный шум.

Slither не найдёт логическую уязвимость. Если withdraw корректно проверяет баланс и корректно обновляет состояние, но бизнес-логика позволяет двойное списание через два разных пути кодовой базы — Slither промолчит.

Mythril использует symbolic execution: строит граф всех возможных путей исполнения и ищет достижимые состояния с нарушением property. Работает хорошо на изолированных контрактах. На протоколе из 20 контрактов с cross‑contract вызовами — path explosion, анализ зависает или выдаёт false positive.

Оба инструмента обязательны как первый pass. Но они не заменяют ручной анализ.

Fuzzing: где Echidna и Foundry находят реальные баги

Echidna — property‑based fuzzer от Trail of Bits. Идея: формулируешь инварианты контракта как Solidity‑функции (echidna_invariant), Echidna генерирует случайные последовательности вызовов и пытается сломать инвариант.

Пример инварианта для lending протокола:

function echidna_total_assets_ge_liabilities() public view returns (bool) {
    return totalAssets() >= totalLiabilities();
}

Echidna найдёт последовательность deposit → borrow → liquidate → repay, которая нарушает этот инвариант. Руками такой кейс не построишь — комбинаций слишком много.

Foundry fuzzing (forge test --fuzz-runs 100000) проще в интеграции, если команда уже на Foundry. Поддерживает stateful fuzzing через invariant тесты. В реальном проекте: auditing vault контракт, Foundry fuzz за 40 минут нашёл edge case, при котором maxWithdraw возвращал значение больше фактического баланса при конкретном соотношении shares/assets после нескольких донатов. Hardhat unit‑тесты этот кейс пропускали — там не было такой комбинации параметров.

Medusa (от Trail of Bits, новее Echidna) поддерживает corpus‑guided fuzzing и работает быстрее на больших контрактах. Если объём кодовой базы > 5000 строк Solidity — смотрим на Medusa.

Как инварианты помогают выявить критические уязвимости

Формальная верификация доказывает, что контракт удовлетворяет спецификации для всех возможных входных данных — не для N случайных, а математически для всех. Инструменты: Certora Prover, K Framework, Halmos.

Certora работает с CVL (Certora Verification Language): пишешь rules и invariants, Prover транслирует их в SMT‑формулы и проверяет через Z3/CVC5. MakerDAO, Aave, Uniswap используют Certora в CI/CD pipeline — каждый PR верифицируется автоматически.

Ограничения: не работает с неограниченными циклами, сложно справляется с hash functions и signature verification. Для контрактов с простой математикой (AMM, lending) — отлично. Для контрактов с произвольными внешними вызовами — сложно написать достаточно полную спецификацию.

Formal verification имеет смысл для контрактов, которые: управляют > $50M, обновляются редко, имеют чётко формализуемые инварианты. Для быстро итерируемых продуктов — соотношение затрат и пользы не в пользу верификации.

Векторы атак, которые пропускают джуниор‑аудиторы

Storage collision в proxy паттерне. Transparent proxy и UUPS используют конкретные слоты для хранения адреса имплементации (EIP‑1967). Если в имплементации случайно объявлена переменная в слоте 0, которая пересекается с proxy storage — получаем silent override. Slither это не поймает, если proxy и имплементация в разных файлах.

Read‑only reentrancy. Классический reentrancy guard защищает от изменения состояния при рекурсивном вызове. Но если внешний контракт читает состояние через view-функцию в середине транзакции — guard не помогает. Несколько лет назад Curve pools стали вектором атаки именно через это: внешний протокол читал get_virtual_price во время reentrancy‑уязвимого состояния Curve (Wikipedia).

Oracle manipulation через TWAP. Spot price — стандартная цель для flash loan атаки (Wikipedia). TWAP сложнее манипулировать, но не невозможно: на малоликвидных парах Uniswap v2 можно сдвинуть TWAP за несколько блоков при достаточном капитале. Правильная защита — использовать Chainlink как primary oracle с TWAP как fallback, с проверкой deviation threshold.

Gas griefing на unbounded loop. Функция итерируется по массиву пользователей. Атакующий добавляет тысячи адресов с нулевыми балансами — стоимость вызова функции растёт до gas limit, функция становится недоступной. Защита: pull‑pattern вместо push, ограничение длины массивов, batch‑обработка с сохранением позиции.

Front‑running на MEV. Транзакция видна в mempool до включения в блок. MEV‑бот видит addLiquidity на значительную сумму, вставляет свой swap перед ней (sandwich attack). Для AMM это часть модели. Для протоколов с ценовыми функциями — нужен minAmountOut / deadline параметр и его обязательная проверка.

Структура полного аудита

  1. Scope definition и автоматический анализ (1‑2 дня). Фиксируем commit hash, версию компилятора, список out‑of‑scope. Запускаем Slither, Mythril, Aderyn. Triage: отделяем реальные критические баги от false positive. Составляем карту зависимостей контрактов.

  2. Ручной анализ (5‑15 дней). Каждый контракт построчно. Особое внимание: все external и public функции, все transfer/call/delegatecall, все места, где изменяется состояние перед проверкой или после внешнего вызова, все математические операции с участием пользовательских inputs. В среднем 95% найденных уязвимостей — логические, а не технические.

  3. Fuzzing и тестирование (2‑5 дней). Echidna или Foundry invariant tests для критических инвариантов. Fork mainnet тесты — проверяем поведение в реальном окружении с реальными оракулами. Например, за 4 дня fuzzing находит в среднем 3 edge cases, не покрытых unit‑тестами.

  4. Отчёт и митигация. Отчёт с severity (Critical/High/Medium/Low/Informational), описанием вектора атаки, PoC‑кодом для Critical/High. Разработчики исправляют, аудиторы делают re‑audit исправлений.

Severity Примеры Требует ли re‑audit
Critical Drain funds, unauthorized ownership transfer Всегда
High Manipulation, DoS на ключевые функции Всегда
Medium Некорректное поведение при edge cases Рекомендуется
Low Газ‑неэффективность, опечатки в events По желанию

Аудит в CI/CD

Нормальная практика для зрелых протоколов: Slither и Aderyn запускаются в GitHub Actions на каждый PR. Certora Prover — на merge в main. Это не заменяет полный аудит перед деплоем, но ловит регрессии.

# .github/workflows/audit.yml
- name: Run Slither
  uses: crytic/[email protected]
  with:
    target: 'src/'
    slither-args: '--filter-paths "test|mock|script"'
Чек‑лист обязательных проверок перед деплоем
  • Все external функции имеют проверки доступа (onlyOwner, onlyRole)
  • Использование SafeERC20 для внешних токенов
  • Отсутствие delegatecall на неизвестные адреса
  • Проверка на reentrancy во всех функциях с внешними вызовами
  • Наличие minAmountOut и deadline в AMM‑функциях
  • Использование проверенного оракула (Chainlink) с deviation threshold

Инструменты аудита: сравнение

Инструмент Тип анализа Что находит Ограничения
Slither Статический Reentrancy, integer overflow, access control Пропускает логические уязвимости
Mythril Symbolic execution Достижимые состояния с нарушением property Path explosion на больших базах
Echidna Fuzzing (property‑based) Нарушение инвариантов Требует написания инвариантов
Certora Formal verification Математическое доказательство свойств Не работает с хешами/подписями

Что входит в работу (deliverables)

  • Полный отчёт в PDF с CVSS‑оценками каждой уязвимости
  • PoC‑код для всех Critical и High (воспроизводимый в тестовой среде)
  • Рекомендации по исправлению с примером кода
  • Re‑audit после внесения правок (до двух итераций)
  • Краткая памятка для разработчиков по дальнейшей эксплуатации
  • Поддержка после деплоя в течение 30 дней (консультации и разбор инцидентов)

Сроки

Аудит простого токена или NFT‑контракта — 3‑5 рабочих дней. DeFi протокол с lending/AMM — 2‑4 недели. Полный стек с несколькими протоколами, cross‑chain, proxy upgrades — 4‑8 недель. Re‑audit исправлений — 3‑7 дней отдельно.

Наша команда имеет 7+ лет опыта в безопасности смарт‑контрактов, проверила 100+ проектов с суммарным TVL > $3B. Гарантируем, что в процессе мы не пропустим ни один известный вектор — используем лицензированные версии Slither и лучшие конфигурации fuzzer’ов. Предотвращённые убытки для клиентов оцениваются более чем в $50M.

Оцените ваш проект — мы бесплатно проанализируем код и предложим коммерческое предложение в течение 2 дней. Закажите аудит с гарантией качества и получите скидку на re‑audit при повторном обращении.