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

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску 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.

Замовте консультацію з інтеграції нашої системи. Зв'яжіться з нами, щоб обговорити ваш проєкт. Отримайте демо-доступ до системи та оцініть її ефективність.

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

Коли протокол втрачає значні кошти через 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 має сенс для контрактів, які: керують значним TVL, оновлюються рідко, мають чітко формалізовані інваріанти. Для продуктів, що швидко ітеруються, співвідношення витрат і користі не на користь верифікації.

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

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.

Oracle manipulation через TWAP. Spot price — стандартна ціль для flash loan атаки. 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 Виведення коштів, несанкціоноване перенесення власності Завжди
High Маніпуляція, 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 понад значну суму. Гарантуємо, що в процесі ми не пропустимо жоден відомий вектор — використовуємо ліцензовані версії Slither та найкращі конфігурації fuzzer'ів. Запобігнуті збитки для клієнтів оцінюються в значну суму.

Оцініть ваш проект — ми безкоштовно проаналізуємо код і запропонуємо комерційну пропозицію протягом 2 днів. Замовте аудит з гарантією якості та отримайте знижку на re‑audit при повторному зверненні.